在python中构造海洋建模代码



我开始使用python进行数值模拟,特别是我从这个项目开始构建我的项目,这将比这更复杂,因为我将不得不尝试许多不同的方法和配置。我全职研究Fortran90代码和Matlab代码,这是我的两种语言;母语";。在这两种语言中,一个人可以随心所欲地构建代码,我正试图模仿这一功能,因为在我的领域(计算海洋学),事情很容易变得相当复杂。例如,请参阅我每天使用的代码NEMO(此处为主页,此处为源代码)。(NEMO的)源代码被方便地划分在文件夹中,每个文件夹都包含特定任务的模块和方法(例如,域离散化例程在文件夹DOM中,垂直物理在文件夹ZDF中,横向物理在文件夹LDF中,等等),这是因为所涉及的过程(物理或纯数学)完全不同。我想建立的是这个

  • /shallow_water_model-
    • create_conf.py(在/cfgs中创建一个具有给定名称的新子目录,如"caspian_sea"或"mediteranean_sea",并将文件夹/src的内容复制到该新子目录中以创建新配置)
    • /cfgs-
      • /capian_sea(示例配置)
      • /mediterranean_sea(配置示例)
    • /src-
      • swm_main.py(初始化字典并调用函数)
      • swm_param.py(填字典)
      • /域-
        • swm_grid.py(创建一个数字网格)
      • /动力学-
        • swm_adv.py(创建平流矩阵)
        • swm_dif.py(创建扩散矩阵)
      • /求解器-
        • swm_rk4.py(带Runge-Kutta4的时间步进)
        • swm_pc.py(带预测器-校正器的时间步进)
      • /IO-
        • swm_input.py(处理netCDF输入)
        • sim_output.py(处理netCDF输出)

脚本create_conf.py包含以下结构,它应该从终端输入一个字符串,创建一个具有该名称的文件夹,并复制其中/src文件夹的所有文件和子目录,这样就可以将该配置的所有输入文件放在那里,并最终修改源代码,为该配置创建一个特别的源代码。这种源代码的重复在海洋建模社区中很常见,因为两种不同的配置(如地中海和里海)不仅在输入文件(如地形、海岸线等)上可能不同,而且在建模本身也可能不同,这意味着您需要对每种配置的源代码进行大量修改。(大多数海洋模型允许您将自己修改的源文件放在特定的文件夹中,并指示它们在编译时覆盖特定的文件。我的代码将足够简单,只需复制源代码。)

import os, sys
import shutil
def create_conf(conf_name="new_config"):
cfg_dir = os.getcwd() + "/cfgs/"  
# Check if configuration exists
try:
os.makedirs(cfg_dir + conf_name)
print("Configuration " + conf_name + " correctly created")
except FileExistsError:
# directory already exists
# Handles overwriting, duplicates or stop
# make a copy of "/src" into the new folder
return
# This is supposed to be used directly from the terminal
if __name__ == '__main__':
filename = sys.argv[1]
create_conf(filename)

脚本swm_main.py可以被认为是对必要例程的调用列表,这取决于您想要考虑的进程类型,就像一样

import numpy as np
from DOM.swm_domain import set_grid
from swm_param import set_param, set_timestep, set_viscosity
# initialize dictionary (i.e. structure) containing all the parameters of the run 
global param
param = dict()
# define the parameters (i.e. call swm_param.py)
set_param(param)
# Create the grid
set_grid(param)

调用的两个例程只获取param的一个特定字段,并为其分配一个值,如

import numpy as np
import os
def set_param(param):
param['nx'] = 32               # number of grid points in x-direction
param['ny'] = 32               # number of grid points in y-direction  
return param

现在,讨论的主要话题是如何在python中实现这种结构。我几乎总是发现源代码要么是单片的(所有例程都在同一个文件中),要么是同一文件夹中的一系列文件。我想有一些更好的组织,但我发现浏览的解决方案用文件夹__pycache__填充/src中的每个子文件夹,我需要在每个文件夹中放一个__init__.py文件。我不知道为什么,但这两件事让我觉得这种方法有些草率。此外,我需要在每个文件中导入模块(比如numpy),我想知道这是否有效。

你认为保持这种结构并尽可能简单会更好吗?感谢您的帮助

据我所知,这里的实际问题是:

我在浏览中找到的解决方案用文件夹__pycache__填充/src中的每个子文件夹,我需要在每个文件夹中放一个__init__.py文件。。。这让我觉得这种方法有些草率。

将代码制作成包并没有什么草率或不符合语法的地方。为了能够从目录中的.py文件导入,必须满足以下两个条件之一:

  • 目录必须位于sys.path
  • 该目录必须是一个包,并且该包必须是sys.path中某个目录的子目录(或者是sys.path中某个文件夹的子目录的包的子目录)

第一个解决方案的代码通常很粗糙,尽管在测试中通常很合适,并且需要修改sys.path以添加所需的每个目录。这通常很棘手,因为将代码放入包中的整个是包结构对源代码中的一些自然划分进行编码:例如,包modeller在概念上与包quickgui不同,并且每个包都可以在不同的程序中独立使用。

将目录放入包中最简单的[1]方法是在其中放置一个__init__.py。该文件应包含概念上属于包级别的任何内容,即不属于模块。将其留空可能是合适的,但从模块中导入公共函数/类/var通常是个好主意,这样您就可以执行from mypkg import thing而不是from mypkg.module import thing。包在概念上应该是完整的,这通常意味着你应该能够(理论上)从多个地方使用它们。有时你不想要一个单独的包:你只想要一个命名约定,比如gui_tools.pygui_constants.pymodel_tools.pymodel_constants.py等。__pycache__文件夹只是python缓存字节码,以使未来的导入更快:你可以移动或阻止它,但只需将*__pycache__*添加到.gitignore中,然后忘记它们。

最后,由于您来自不同的语言:

  • 许多由科学家(而不是程序员)编写的python代码都是非常不符合语法的IMHO。数十亿行长的单个python文件是而不是好风格[2]。Python更喜欢可读性,总是:调用derived_model而不是dm1。如果你这样做,你很可能会发现你不需要你想象的那么多dirs
  • 在每个文件中导入相同的模块是一个微不足道的成本:python导入一次:每隔一次导入只是sys.modules中绑定的另一个名称。始终显式导入
  • 一般来说,不要再担心python的性能了。尽可能清楚地编写代码,如果需要的话,可以对其进行评测,并找出速度较慢的地方。Python是如此之高,以至于在编译语言中学习的微观优化可能会适得其反
  • 最后,这主要是针对个人的,不要用大写字母给出文件夹/模块的名称。FORTRAN可能会鼓励这样做,而且它是在那些通常不具有文件名大小写敏感性的机器上编写的,但我们不再有这些限制。在python中,我们为常量保留大写字母,所以当我不得不修改执行大写字母时,我觉得很奇怪。同样,"DOM"让我想到了文档对象模型,这可能不是你在这里的意思

参考文献

[1] Python确实有隐式命名空间包,但您最好使用显式包来表示您想要制作包的意图(并避免各种导入问题)。

[2] 有关如何构建事物的更多约定,请参见pep8。我还建议查看一些不错的通用库,看看它们是如何工作的:它们往往是由专注于编写干净、可维护代码的主流程序员编写的,而不是由专注于解决高度特定(通常非常复杂)问题的科学家编写的。

最新更新