Bash와 C++로 Projected-Hartree-Fock 자동화 파이프라인 구축하기

외부 인자를 받아 실행되는 Bash 스크립트는 C/C++의 main(int argc, char* argv[])와 동일한 방식으로 동작합니다. 이를 활용해 Projected-Hartree-Fock 계산을 자동화하는 파이프라인을 구성해보겠습니다.

단일 핵종 PHF 계산 스크립트

Calvin Johnson 연구팀이 개발한 PHF 패키지는 두 개의 핵심 실행 파일로 구성됩니다. Hartree-Fock 부분인 Sherpa와 투영(projection)을 담당하는 LAMP입니다. 먼저 단일 핵종을 처리하는 run_phf_single.sh를 작성합니다.

#!/bin/bash

# Usage: ./run_phf_single.sh <sps_file> <proton_count> <neutron_count> [<interaction> <core_mass>]

orbitals=$1
valence_p=$2
valence_n=$3
hamiltonian=${4:-usdb}
core_A=${5:-16}

target="p${valence_p}n${valence_n}"
mass_ref=$((core_A + 2))
mass_full=$((core_A + valence_p + valence_n))
scale_factor=0.3
angular_max=20

echo "=== Initializing SHERPA for ${target} ==="

# 기존 산출물 제거 (상호작용 프롬프트 방지)
rm -f ${target}.sd ${target}.res

./sherpa19.x << EOF
${orbitals}
${valence_p}
${valence_n}
n
i
1
${hamiltonian}
1 ${mass_ref} ${mass_full} ${scale_factor}
n
test
19911
1000
15
-1
w
${target}
${hamiltonian}
x
r
e
x
x
EOF

mv hf.basis ${target}.hf.basis
echo "Hartree-Fock basis saved: ${target}.hf.basis"

echo "=== Launching LAMP projection ==="

./lamp.x << EOF
${orbitals}
${valence_p} ${valence_n}
1
${target}
${angular_max}
n
y
${hamiltonian}
1 ${mass_ref} ${mass_full} ${scale_factor}
end
0.0001
20
0.001
y
${target}
n
EOF

mkdir -p results
mv ${target}.res results/
echo "Completed: ${target}.res stored in results/"

sd-shell 순회 자동화

이제 N ≥ Z 조건을 만족하는 sd-shell 짝짝수 핵종을 순회하는 상위 스크립트를 작성합니다.

#!/bin/bash

echo "PHF batch calculation started at $(date '+%Y-%m-%d %H:%M:%S')"

for p_count in {0..6}; do
    for n_count in $(seq ${p_count} 6); do
        num_protons=$((p_count * 2))
        num_neutrons=$((n_count * 2))
        
        echo "Processing: p=${num_protons}, n=${num_neutrons}"
        bash run_phf_single.sh sd ${num_protons} ${num_neutrons} usdb 16
        
    done
done

echo "All calculations finished at $(date '+%Y-%m-%d %H:%M:%S')"

결과 파일 파싱: C++ 추출 유틸리티

생성된 .res 파일에서 기대 에너지를 추출하는 C++ 유틸리티입니다. 파일명 패턴과 파싱 로직을 모듈화하여 재사용성을 높였습니다.

#include <fstream>
#include <sstream>
#include <iostream>
#include <string>
#include <iomanip>

using namespace std;

string ConstructFilename(int proton_valence, int neutron_valence) {
    const string prefix = "results/p";
    const string middle = "n";
    const string suffix = ".res";
    
    auto to_digits = [](int val) -> string {
        if (val < 10) return string(1, '0' + val);
        return to_string(val);
    };
    
    return prefix + to_digits(proton_valence) 
         + middle + to_digits(neutron_valence) + suffix;
}

void ExtractGroundStateEnergies(double energy_table[7][7]) {
    const string sentinel = "State";
    const string delimiter_line(60, '-');
    
    for (int p = 0; p <= 12; p += 2) {
        for (int n = p; n <= 12; n += 2) {
            
            string filepath = ConstructFilename(p, n);
            ifstream reader(filepath);
            
            if (!reader.is_open()) {
                cerr << "Warning: Cannot access " << filepath << endl;
                continue;
            }
            
            string buffer, keyword;
            bool found_header = false;
            
            // 헤더 탐색: "State" 키워드 검출
            while (getline(reader, buffer)) {
                istringline parser(buffer);
                parser >> keyword;
                if (keyword == sentinel) {
                    found_header = true;
                    break;
                }
            }
            
            if (!found_header) {
                cerr << "Malformed output: " << filepath << endl;
                reader.close();
                continue;
            }
            
            // 구분선 및 데이터 행 스킵
            getline(reader, buffer);  // separator
            
            // 실제 에너지 값 읽기
            getline(reader, buffer);
            istringstream data_stream(buffer);
            int level_idx;
            double ground_energy;
            data_stream >> level_idx >> ground_energy;
            
            int p_idx = p / 2;
            int n_idx = n / 2;
            energy_table[p_idx][n_idx] = ground_energy;
            energy_table[n_idx][p_idx] = ground_energy;  // 대칭성 활용
            
            cout << fixed << setprecision(4);
            cout << "Extracted [" << p << "," << n << "]: " 
                 << ground_energy << " MeV" << endl;
            
            reader.close();
        }
    }
}

데이터 비교 파이프라인

추출된 PHF 기대 에너지와 기존 shell-model 대각화 결과를 비교하여 상관 에너지를 정량화할 수 있습니다. 두 방법론 간 에너지 차이 ΔE = E_PHF - E_SM는 PHF가 shell-model 상限으로부터 개선되는 정도를 나타냅니다.

void ComputeEnergyDeficiency(
    const double phf_data[7][7],
    const double sm_data[7][7],
    double discrepancy[7][7]
) {
    for (int i = 0; i < 7; ++i) {
        for (int j = 0; j < 7; ++j) {
            discrepancy[i][j] = phf_data[i][j] - sm_data[i][j];
        }
    }
}

이 파이프라인을 통해 sd-shell 전역에서의 PHF 성능을 체계적으로 평가할 수 있습니다.

태그: bash Projected-Hartree-Fock shell-model nuclear-physics C++

7월 13일 20:52에 게시됨