psolve命令行参数说明

· 预计阅读 9 分钟

pSolve 软件包主要用于天体测量和大地测量 VLBI 数据的解算与分析,是 SOLVE 软件包系列的一个变体。psolve 是其主要命令行入口,通过调用其他程序进入后续解算流程。由于旧版用户指南和当前代码实现已不完全一致,因此这里我试图结合源代码,说明该指令的运行逻辑。

这里讨论的 pSolve 软件版本是 20260812。文中用 pSolve 表示软件包,用 psolve 表示命令或启动脚本。

在 VLBI 数据处理中,我们通常会提到交互解和全局解。从程序的运行方式来看,交互解采用交互模式,全局解通常采用批处理模式。 psolve 根据输入参数选择运行模式,而具体的数据、模型和解算设置则由后续程序及控制文件处理。

首先,我们从安装过程谈起。下图是pSolve的安装流程: pSolve的安装流程

psolve 是一个 C shell 启动脚本,由模板 psolve/support/psolve_templ 生成。在我的安装环境中,其完整路径为 /opt64/psolve/bin/psolve。 生成脚本的代码位于psolve/support/config.csh,核心代码是:

cat $SOLVE_ROOT/support/psolve_templ | \
    sed "s@%%@Automatically generated on $DATE_ISO from support/psolve_templ@g" | \
    sed "s@%SOLVE_PROG_DIR_VAL%@$SOLVE_PREFIX/psolve/bin@g" | \
    sed "s@%SOLVE_VERSION%@$SOLVE_VERSION@g" \
    > $SOLVE_ROOT/temp/psolve

chmod u+rwx,g+rwx,o+rx $SOLVE_ROOT/temp/psolve

这几行依次完成:

  • cat 读取启动脚本模板 support/psolve_templ。
  • 第一个 sed 写入自动生成说明及生成时间。
  • 第二个 sed 将程序目录占位符替换成实际安装目录。
  • 第三个 sed 将版本占位符替换成软件版本。
  • > 将处理后的文本写入 temp/psolve。
  • chmod 为文件所有者和所属组添加读、写、执行权限,为其他用户添加读、执行权限。

这里的 SOLVE_ROOT 是 pSolve 源码目录,SOLVE_PREFIX 是安装前缀。因此,psolve 启动脚本是在配置阶段通过文本替换生成的。接着,make install 将生成的脚本复制到安装目录。安装规则见 psolve/Makefile.in2,其中相关操作是:

cp $(SOLVE_ROOT)/temp/psolve \
   $(SOLVE_PREFIX)/psolve/bin/psolve

如果通过 sgdass_install.py 安装,并采用示例配置中的安装前缀 /opt64,安装后的文件就是 /opt64/psolve/bin/psolve。如果修改了安装前缀,路径也会随之改变。

理解 psolve 指令的参数处理和启动逻辑,可以直接读取这个文件:

cat /opt64/psolve/bin/psolve

当前 psolve 最多接受三个位置参数:

psolve <Solve_initials> [<control_file>] [verbosity_level]

具体分支由下面的 C shell 语句决定:

switch ( $#argv )

其中,$#argv 表示传入脚本的参数个数。旧版用户指南中还列出了第四、第五个参数,即处理器编号和处理器总数,但当前这份 psolve 脚本不接受这两个参数。

输入形式 脚本行为
psolve 打印用法,退出码为 1
psolve -v 或 psolve --version 输出 20260812,退出码为 0
psolve AU 启动交互模式,进行交互解
psolve AU global.cnt 启动批处理模式,按控制文件进行全局解等处理
psolve AU global.cnt silent 等 启动批处理模式,并设置输出与续算策略
四个或更多参数 打印用法,退出码为 1

其中,-v 仅在作为唯一参数时表示查询版本,不是 verbose 的缩写。当前脚本也没有单独实现 -h 或 --help 分支。

第一个参数 <Solve_initials> 是由两个字符组成的运行标识,通常记为 XX;上例中的 AU 就是一组 initials。它标识一组工作文件和运行状态,例如 CONFXX、PRGFXX、STATXX,以及 spool 输出的后缀。它不是操作系统用户名,也不是控制文件中的 solution ID,完全由用户自定义。该标识可以区分同一台机器上同时运行的解算产生的中间文件。

enter 程序将输入转为大写,存入两字符变量 LETT,随后调用 LETOK 校验。实际使用时,应恰好提供两个已注册字符,不要依赖截断行为。能够使用的 initials 都记录在 letok 文件,因而通过编辑该文件就可以添加新用户。LETOK 优先通过环境变量 PSOLVE_SAVE_DIR 指定的目录来寻找 letok 文件;未设置时,采用编译时确定的 SOLVE_SAVE_DIR。工作目录也类似:优先使用环境变量 PSOLVE_WORK_DIR,否则使用编译时确定的 SOLVE_WORK_DIR。

第二个参数 <control_file> 是批处理控制文件的路径,用于指定数据、模型、待估参数和输出等设置。脚本首先检查该路径是否对应一个普通文件,然后将其传给 enter。上面的 global.cnt 只是文件名示例,具体解算设置由文件内容决定。

下面主要讨论第三个参数,它同时控制信息输出和续算策略。当前脚本将 noenter 固定设为 no,后续没有修改它,因此实际执行的是调用 enter 的分支。脚本中虽然保留了直接调用 BATCH 的代码,但这些分支在当前设置下不会执行。

令 I 表示 initials、C 表示控制文件,六种参数对应的实际调用如下。为简洁起见,表中省略了 enter 前面的程序目录。

第三个参数 作用 BATCH_SILENT 实际调用
verbose 正常输出信息;按普通流程判断是否续算,必要时询问用户 NO enter I C 0 b 0 1 1
silent 减少信息输出;续算策略与 verbose 相同 YES enter I C 0 b 0 1 1
verbose-autorestart 正常输出信息;具备续算条件时自动续算,无须用户确认 NO enter I C 0 a 0 1 1
silent-autorestart 减少信息输出;具备续算条件时自动续算,无须用户确认 YES enter I C 0 a 0 1 1
verbose-norestart 正常输出信息;不续接上次中断的解算,重新开始 NO enter I C 0 n 0 1 1
silent-norestart 减少信息输出;不续接上次中断的解算,重新开始 YES enter I C 0 n 0 1 1

这里有两条独立的传递途径:

  • 信息输出通过环境变量 BATCH_SILENT 传递。
  • 续算策略通过 enter 的第四个实参传递。 其中,b 表示普通批处理,是否续算按正常流程判断,必要时询问用户;a 表示在具备续算条件时自动续算,不再询问用户;n 表示不续接上次中断的解算,而是重新开始。

BATCH_SILENT 是环境变量。脚本使用 C shell 的 setenv 命令设置它:

setenv BATCH_SILENT YES

silent 系列将它设为 YES,verbose 系列则将它设为 NO。该变量会被后续启动的 enter 及其子进程继承,但不会反过来修改调用 psolve 的父 shell 环境。

在 Fortran 程序中,例如 psolve/progs/solve/batch/ctrlfl.f 和 ctrls.f,读取和处理该变量的代码为:

BATSIL = '   '
CALL GETENVAR ( "BATCH_SILENT", BATSIL )
IF ( BATSIL(1:1) .EQ. 'y' ) BATSIL(1:1) = 'Y'

因此,BATCH_SILENT 是环境变量名,BATSIL 是 Fortran 程序内部的字符变量名。代码先将 BATSIL 初始化为空白,再读取环境变量,将首字符的小写 y 转为大写 Y,随后通过判断 BATSIL(1:1) 是否为 'Y',决定是否输出受该条件控制的信息。

这里需要注意,silent 并不意味着程序完全没有终端输出。它只抑制代码中受相关条件控制的信息,也不等于自动取消续算询问。需要自动续算时,应使用带 -autorestart 后缀的参数。

那么,省略第三个参数会怎样? 例如:

psolve AU global.cnt

此时,脚本进入两个参数的分支,打印主机名、启动时间和控制文件名,然后调用:

${SOLVE_BIN}/enter AU global.cnt 0 b 0 1 1

这个分支没有设置 BATCH_SILENT,因此会保留已有环境中的状态。 如果在 Bash 中先设置:

export BATCH_SILENT=YES
psolve AU global.cnt

脚本就会把已有的 YES 继续传给后续程序。export 设置的值也会供当前终端随后启动的其他子进程使用。 也可以只对一次命令设置:

BATCH_SILENT=YES psolve AU global.cnt

如果环境中没有设置 BATCH_SILENT,前述 Fortran 代码中的 BATSIL 保持空白,不满足首字符为 'Y' 的条件,因此这些位置保留正常信息输出。 这也解释了为什么用户指南将 verbose 称为默认行为。不过,从脚本实现来看,省略第三个参数并不等于强制设置 BATCH_SILENT=NO,因为已有的环境值仍然可能影响运行。

此外,两个参数分支中的启动信息由脚本直接通过 echo 打印,没有检查 BATCH_SILENT。因此,即使继承了 YES,这些启动信息仍会显示。

如果显式指定第三个参数,脚本会覆盖继承的环境值。例如:

BATCH_SILENT=YES psolve AU global.cnt verbose

虽然启动时传入了 YES,但 verbose 分支会在脚本进程中将其重新设为 NO,后续程序因而采用相应的正常信息输出行为。 因此,为了避免运行行为受到已有环境变量的影响,最好每次都显式指定第三个参数。例如,采用普通批处理和正常信息输出:

psolve AU global.cnt verbose

在具备续算条件时自动续算,并保留信息输出:

psolve AU global.cnt verbose-autorestart

重新开始解算,并减少信息输出:

psolve AU global.cnt silent-norestart

这些参数明确指定了本次运行的信息输出与续算策略,便于在命令记录和批处理脚本中检查。