如何以正确的方式将求解器与计算实验分开?

计算科学 实验
2021-12-23 16:01:27

我作为一名博士生从事计算工作,我试图找到一种正确的方法,将代码(求解器)与基于该求解器的计算实验分开。

基本上,我所做的每个项目都围绕一个特定的求解器(MATLAB 或 Python 代码),并且对于每个项目,都应该进行一组实验。之前,我想通过以下方式进行操作:

project
    01-2016-01-01-task-1
    02-2016-01-01-task-2
    03-2016-01-03-task-3

这样每个子文件夹对应于我所做的一个特定任务(或实验)。每个子文件夹都包含求解器的所有输出和求解器代码本身,因为我经常需要为特定实验修改求解器。

我已经推断出修改求解器代码的最佳方法是向后兼容的方式,这样如果实验需要我更改代码以使用例如不同的时间积分器(我求解 PDE),那么应该向求解器添加一个新的配置参数,该参数定义在运行时使用的时间积分器。

但是,我对这种方法有一个问题(为每个实验保留本地版本的求解器):我必须将所有这些不同的版本合并在一起,现在我手动完成。最近,我想到一个更好的组织是

project
    code
    tasks
        01-2016-01-01-task-01
        ...
        27-2016-03-06-task-27

也就是说,代码应该是集中的和版本控制的。

现在想象以下情况。实验N要求我修改代码,然后使用不同的参数运行求解器 3 次,每次运行需要 20 分钟。我使用 bash 脚本执行此操作,该脚本按顺序运行求解器三次。与此同时,我开始实验N+1这也需要修改代码。我开始修改代码,它可以打破实验N(例如,在运行 1 和运行 2 之间)。

你如何处理这种情况?为每个实验分叉求解器代码并将其保存在实验子文件夹下会更好吗?

2016-09-28 更新:根据我得到的答案和评论,似乎问题写得不够清楚,尤其是标题。

我的问题是关于如何基于模拟代码进行实验,其中代码必须与项目的进展同时发展。实际上,通常,代码从一开始就承认一些通用性(例如,所有问题参数都可以指定为命令行参数,以及一些求解器参数)。然而,在一系列实验之后,代码很可能需要进一步的泛化,例如,一个有限差分近似应该被另一个替换。在这种情况下,正如下面许多人指出的那样,最好的解决方案是向求解器添加一个选项,该选项在运行时选择所需的近似值。

因此,最好对其进行版本控制,并为每个实验隔离一份代码副本。这就是我的问题所在:如何处理每个实验的多个代码副本?

如果您不使用任何版本控制系统,您可以将代码复制到每个实验的子目录中,然后手动将任何修改合并到代码的“黄金”副本(这是我以前的方法)。

如果您使用版本控制系统 Git,您可以制作存储库的多个克隆以具有独立的副本。然而,由于 Git 2.5+ 提供了一个拥有多个存储库工作副本的功能,我在下面写的答案提出了更优雅的方法。

3个回答

尽管 Greg Wilson 在 @nicoguaro 的回答中发表的文章提供了很好的建议,但我还认为,您所描述的情况最好通过为您的求解器提供运行时配置选项来处理。Jed Brown(也是 SciComp 用户)的这篇文章描述了一些良好的高级实践,您还可以在http://www.mcs.anl.gov/uploads/cels/papers/P2010中查看使用 PETSc 的运行时配置示例-0112.pdf

基本思想是,如果您要更改参数,这些都应该是运行时选项,因此您可以运行代码./mycode -parameter_a 1.7 -parameter_b 2.5并在运行时再次使用./mycode -parameter_a 2.6 -parameter_b 3.4. 在理想情况下,您永远不必重新编译代码来运行实验。在实践中,当然,设置运行时选项需要一些基础设施,但如果你能做到,那么这项投资是值得的,因为它将使运行具有不同参数值的并发实验变得容易。

感谢大家的评论。我找到了以下很好的解决方案来解决我的问题。显然,每个任务都应该有自己的代码版本,以将任务彼此隔离并确保可重复性,因为必须始终清楚该任务使用了哪个版本的代码。剩下的问题是:如何避免多次克隆 git 存储库?

Git 2.5+ 版允许创建连接到同一个存储库的多个工作树。使用多个工作树,您可以避免多次克隆存储库,同时在单独的分支中跟踪代码的所有版本。

因此,项目的结构将是这样的:

project
    code
    tasks
        01-2016-01-01-task-01
            code
        ...
        27-2016-03-06-task-27
            code

每个目录01-2016-01-01-task-01和类似目录都有自己的代码副本设置到自己的分支。

要创建其他工作树,请使用以下 git 命令:

cd <project>/code
git worktree add -b 01-2016-01-01-task-01 \
    ../tasks/01-2016-01-01-task-01/code master

01-2016-01-01-task-01它从中创建一个分支master并将其签出到目录../tasks/01-2016-01-01-task-01/code中。

然后,当给定任务的工作完成时,您可以将相应的分支合并到master分支中。

您的部分问题在最近的出版物“科学计算中的足够好的实践”(参考文献 1)中得到了解决,主要是在第 4 点中。不过,我添加了整个列表。

  1. 数据管理
    1. 保存原始数据。
    2. 创建您希望在世界上看到的数据
    3. 创建便于分析的数据。
    4. 记录用于处理数据的所有步骤。
    5. 预计需要使用多个表。
    6. 将数据提交到信誉良好的 DOI 发布存储库,以便其他人可以访问和引用它。
  2. 软件
    1. 在每个程序的开头放置一个简短的解释性注释。
    2. 将程序分解为函数。
    3. 对消除重复毫不留情。
    4. 始终搜索可以满足您需要的维护良好的软件库。
    5. 在依赖它们之前测试库。
    6. 给函数和变量起有意义的名字。
    7. 明确依赖性和要求。
    8. 不要注释和取消注释代码段来控制程序的行为。
    9. 提供一个简单的示例或测试数据集。
    10. 将代码提交到信誉良好的 DOI 发布存储库。
  3. 合作
    1. 创建项目概览。
    2. 创建一个共享的公共“待办事项”列表。
    3. 明确许可。
    4. 使项目可引用。
  4. 项目组织
    1. 将每个项目放在自己的目录中,该目录以项目命名。
    2. 将与项目关联的文本文档放在 doc 目录中。
    3. 将原始数据和元数据放在数据目录中,将清理和分析过程中生成的文件放在结果目录中。
    4. 将项目源代码放在 src 目录下。
    5. 将外部脚本或编译程序放在 bin 目录中。
    6. 命名文件以反映其内容或功能。
  5. 跟踪变化
    1. (几乎)备份人类创建的所有内容。
    2. 保持小的变化。
    3. 经常分享更改。
    4. 创建、维护和使用清单以保存和共享对项目的更改。
    5. 将每个项目存储在与研究人员的工作机器镜像的文件夹中。
    6. 将名为 CHANGELOG.txt 的文件添加到项目的 docs 子文件夹中。
    7. 只要进行了重大更改,就复制整个项目。
  6. 手稿
    1. 使用具有丰富格式、更改跟踪和参考管理的在线工具撰写手稿。
    2. 在项目的 doc 目录中包含一个 PUBLICATIONS 文件。
    3. 以允许版本控制的纯文本格式编写手稿。

参考

  1. Wilson、Greg、Jennifer Bryan、Karen Cranston、Justin Kitzes、Lex Nederbragt 和 Tracy K. Teal。“科学计算中足够好的实践”。arXiv 预印本 arXiv:1609.00037 (2016)

  2. Wilson、Greg、DA Aruliah、C. Titus Brown、Neil P. Chue Hong、Matt Davis、Richard T. Guy、Steven HD Haddock 等人。“科学计算的最佳实践”。PLoS Biol 12,没有。1 (2014): e1001745。