2021년 11월 25일 목요일

PubMLST 자료를 쉽게 가져오는 방법 - mlst_check(Sanger)

오늘도 나의 2021년 11월 하순을 뜨겁게 달구고 있는 fIDBAC과 관련한 이야기를 써 내려가려고 한다. fIDBAC 파이프라인에서는 16S rRNA 서열 분석과 KmerFinder를 통해서 top 20 closest genome을 고르고, 이를 대상으로 fastANI를 실시하여 일정 컷오프를 통과하는 결과로부터 query genome이 어떠한 species에 해당하는지를 판별하여 그 정보를 OUTDIR/fastANISpecies 파일에 출력한다. example/example.fa를 사용한 경우는 이러하다. 

$ cat example_2021_11_25_08_20_15/fastANISpecies 
Salmonella enterica

fastANISpecies 파일에 여러 라인이 기록될 수도 있을까? fIDBAC.pl(v20181126)의 133번째 줄에서 다음과 같이 fastANISpecies 파일의 첫 줄만을 뽑아내도록 한 것을 보면 가끔 그런 일이 생기기도 하는 모양이다.

my $template=`head -n 1  $result/fastANISpecies`;chomp $template;

fastANISpecies 파일을 만드는 과정에 대해서도 살펴볼 것이 많지만 이에 대해서는 나중에 알아보기로 하고, 오늘은 fIDBAC.pl이 이 파일을 이용하는 다음 단계에 관해서 탐구해 보자. 이 단계에서 fIDABAC.pl 스크립트는 표준 출력으로 다음을 내보낸다.

Salmonella enterica...Salmonella enterica...

마치 강도에 피습당한 피해자가 '나를 칼로 찌른 놈은 아무개였어...'하고 간신히 내뱉고는 숨을 거두는 영화의 한 장면같다. 똑같은 종명(Salmonella enterica)를 두 번에 걸쳐서 출력한 것으로 보이지만 앞의 것은 $name 변수이고 뒤의 것은 $template 변수이다. 어느 종에 대한 MLST 분석을 해야 하는지 결정하는 열쇠가 되는 것이 바로 $name이다.

config_db.txt에서 설정한 LPSN(Species.tax 파일)의 첫 번째 컬럼은 'Salmonella_enterica'와 같은 형식으로 되어 있다. 공백을 underscore로 치환한 것이다. 왜냐하면 MLST DB를 구성하는 각 종명이 디렉토리 형식으로 그대로 쓰여야 하기에, 공백을 그대로 두면 곤란하기 때문이다. 이 문자열은 최종적으로 $species 변수에 저장된다. 일단 Species.txt 파일에서 첫 컬럼을 하나씩 읽어서 underscore를 다시 공백으로 바꾸어 $name과 비교하고, 일치하는 것이 있으면 이를 OUTDIR/Taxonomy.txt에 기록한다. 이는 MLST 함수가 하는 일 중의 하나이다.

MLST 분석은 fIDBAC.pl의 199번 라인부터 시작한다. 

my ($species, $flag) = split (/\t/, MLST ($template));
 if ($flag == 0){
      my $mlstlist = glob "$pubMLSTdb/$species/*.txt" ;
      system("perl $Bin/run_MLST.pipeline.pl -s $species -t $mlstlist -q $input -o $result \n");
  }else{
      print STDERR "This species is not exists in the pubMLST db\n";
} 

여기까지 살펴보면 $template와 $name 변수가 제대로 쓰였음을 알 수 있다. pubMLSTdb가 설치된 디렉토리 하위에는 $species에 해당하는 서브디렉토리가 존재하고, 또 그 밑에 세부적인 파일들(allelic progiles & sequenes)이 있어야 한다. 정확히 말하자면 여기까지가 서론이었다.

config_db.txt에서는 pubMLSTdb의 설치 위치를 지정해 두어야 한다. 그리고 fIDBAC README.md 파일에 의하면 'Download PubMLST database here.'라고 하였다. 그러나 'here'를 클릭하면 XML 파일이 나올 뿐이다. PubMLST 공식 웹사이트의 다운로드 페이지를 가 보아도 전체 자료를 하나로 묶어서 클릭만 하면 쉽게 내려받을 수 있게 만들지 않았다. 흠... 공식으로 제공하는 API를 이용해서 활용할 재주는 나에게 없다. 어떻게 해야 오늘 날짜 기준으로 153개나 되는 데이터베이스를 한꺼번에 가져올 수 있을까? 'bactopia datasets --available_datasets'으로 확인하면 무려 683개나 되는 자료가 있다. 아마 후자의 숫자는 PubMLST가 호스팅하지 않는 다른 것까지 포함해서 그런지도 모른다.

Torsten Seemann의 mlst를 사용하여 다운로드한 자료를 쓸 수 있을까? 이는 직접 실행할 일은 별로 없고, TORMES를 통해서 이용하게 된다. 나의 경우에는 다음의 위치에 파일들이 전부 저장되어 있다.

/opt/miniconda3/envs/tormes-1.3.0/db/pubmlst

그러면 이 디렉토리를 config_db.txt의 pubMLSTdb로 설정하면 될까? 전혀 그렇지 않다. 이 디렉토리의 목록은 다음과 같다. fIDBAC.pl에서는 Salmonela_enterica라는 하위 디렉토리를 이로부터 찾으려고 할 것이지만, 실제 존재하는 디렉토리 명칭은 약칭인 senteriaca이다. 이는 pubMLST 체계에서는 공식적으로 쓰이는 문자열이지만 fIDBAC.pl은 완전한 종명을 원한다.


어쩌겠는가? 완전한 종명 체계로 pubMLST를 다운로드해 주는 유틸리티를 찾아 보아야 한다. 검색 끝에 Sanger Institute에서 개발한 mlst_check라는 것을 발견하였다. 설치는 쉽게 하였으나 역시 SSL 인증서 문제로 직장 내 전산망에서는 다운로드 명령('download_mlst_databases')이 먹히질 않았다. 이를 우회하는 가장 간단한 방법은 노트북 컴퓨터로 집에서 설치한 뒤 직장으로 가지고 나와서 복사하는 것이다. 

이제 fIDBAC이 잘 실행되겠지... 그러나 이는 잘못된 기대였다. T. Seemann의 mlst 명령을 이용한 것과 Sanger의 mlst_check를 이용한 것은 종명 서브디렉토리뿐만 아니라 그 하위의 파일 구조도 다르다는 것을 발견하였다. Salmonalle enterica의 예를 들어 보자.

  • T. Seemann의 스타일: senterica 디렉토리 하위에 aroC.tfa  dnaN.tfa  hemD.tfa...의 파일이 있음
  • Sanger의 스타일: Salmonella_enteria 디렉토리 하위에 profiles/profiles_csv와 alleles/alleles_fasta 두 개의 파일이 있음. 이는 fIDBAC에 의해 쓰이기가 곤란하다. 왜냐하면 '$mlstlist = glob "$pubMLSTdb/$species/*.txt'라는 코드를 상기해 보라.
지금까지 테스트한 바에 따르면, T. Seemann 스타일로 다운로드하되 종명 디렉토리를 Salmonella_enterica로 바꾸어 놓은 것만 제대로 결과를 내었다. 어느 것이 더 바람직한가? (1) T. Seemann 스타일로 다운로드한 뒤 senterica => Salmonella_enterica로 디렉토리명을 바꾸는 것과, (2) Sanger 스타일로 다운로드한 뒤 alleles_fasta 파일을 처리하여 각 유전자별로 aroC.tfa  dnaN.tfa... 파일을 만드는 것. 종명과 그 약자를 연결한 파일이 있다면 (1)의 작업은 아주 쉬울 것이다. 그러나 이는 간편한 텍스트 파일이 아니라 XML로 주어진다. 

이 문제를 해결한 뒤에도 복수의 scheme이 존재하는 종이라든가(예: Acinetobacter_baumannii_1 & Acinetobacter_baumannii_2), spp로 끝나는 scheme을 fIDBAC이 제대로 처리할지는 의문이다.

여기까지 적고 나니 회의감이 밀려온다. 내가 지금 월 하고 있는거지?

회의감을 정리하는 도중 또 경악을 금치 못할 일을 발견하였다. run_MLST.pipeline.pl 스크립트가 pubMLST가 지정된 위치를 별도로 지정하여 사용하는 것이다! config_db.txt는 어디다 갖다 버리고!

2021년 11월 24일 수요일

fIDBAC 실행용 펄 스크립트의 use compiler directive 선언 오류, 그리고 플러스 알파(+베타, 감마...)

fIDBAC(어제 작성한 소개의 글 링크)에서 오류를 찾는 것이 취미가 될 수준에 이르렀다. 나는 파이썬에는 까막눈이지만 Perl은 조금 아는 편이라서 fIDBAC의 주요 스크립트를 뜯어보는 수준은 된다. 메인 스크립트는 fIDBAC.pl이다.

fDIBAC.pl을 처음 실행하게 되면 GACP.pm 모듈을 인식하지 못한다는 에러가 발생한다. 

Can't locate GACP.pm in @INC (you may need to install the GACP module) (@INC contains: ...)

스크립트의 위치로 이동하여 실행을 하거나, 혹은 PERL5LIB 환경변수를 스크립트가 위치한 곳으로 선언하면 일단은 에러가 없어진다. GACP.pm은 config_db.txt 설정 파일을 읽어들여서 중요한 프로그램과 파일을 변수로 저장하는 역할을 한다.

왜 이런 사소한 오류가 뜨는 것일까? fIDBAC.pl 스크립트의 시작 부분을 확인해 보았다.

#!/usr/bin/perl -w
#author:liangq at 20181126 ,modified by liangqian ,at 20181206
use strict ;
use Getopt::Long ;
use Cwd 'abs_path';
use File::Basename ;
use File::Path 'mkpath';
use FindBin '$Bin';
use Data::Dumper;
use List::Util qw/max min/;
use lib $Bin;
use threads;
use GACP qw(parse_config);

특별한 문제는 없다. FindBin을 이용하여 원본 펄 스크립트의 위치를 찾아서 $Bin 변수에 저장하고(작은 따옴표로 둘러싼 것은 별로 마음에 들지 않음), 이어서 use lib $Bin 디렉티브를 선언했으니 펄 스크립트가 있는 디렉토리를 @INC의 맨 앞에 추가한 셈이 된다. 그리고 가장 마지막에서 use GACP를 선언하여 GACP.pm 모듈을 선언하였다. 훌륭하다!

그런데도 여전히 같은 에러가 발생하였다. GACP.pm을 로드하는 스크립트가 또 있나? fIDBAC.pl에 의해 내부적으로 구동되는 다른 스크립트 중에서 select_kmerfinder_16SAndANI.pl와 AR_VF.run.pl도 GACP.pm을 필요로 한다. 스크립트에 오타 같은 것은 없는데 왜 에러가 사라지지를 않는 것일까...

이런! select_kmerfinder_16SAndANI.pl 파일을 열어보니 'use lib $Bin' 라인이 없었다. 무슨 이런 실수를... 개발자는 항상 스크립트 설치 디렉토리에서만 테스트를 했단 말인가? 수준 이하의 오류를 찾아내어 수정하느라 인생을 낭비하는 것만 같아서 이제는 서글프기까지 하다. 빠진 라인을 삽입하였더니 이 오류는 사라졌다.

그러나 아직 끝이 아니다. 

  1. run_rgi.sh가 호출하는 format_RGIresult.pl는 도대체 GitHub 사이트 어디에 숨어 있는가?
  2. config_DB.txt에서 'orthoANI'라는 이름으로 부르는 프로그램은 도대체 무엇을 의미하는가? 내가 발견한 fIDBAC 파이프라인의 문제점 중 가장 심각한 것은 바로 여기에 있다. 
orthoANI의 문제를 좀더 상세하게 알아보자. config_DB.txt 파일을 열어보면 orthoANI는 ANI.pl 펄 스크립트를 지정하는 것으로 보인다. 그러나 OrthoANI라 하면, 보통은 천랩에서 개발한 ANI 계산용 알고리즘을 뜻한다. 이 알고리즘을 프로그램으로 구현한 것은 자바 애플리케이션인 OAT(Orthologous Average Nucleotide Identity Tool)이다. 'ortho-'라는 접두사를 붙여서 불필요한 혼동을 불러 일으키고 있다. 이것이 (2)에 따르는 첫 번째 문제이다.

ANI.pl은 원래 Jaipeng Chen이라는 사람이 JSpecies(GUI Java application)를 참조하여 legacy blast + Perl로 만든 ANI 계산용 스크립트이다(GitHub). 9년 전에 업로드된 상태 그대로 수정되지 않았다. fIDBAC의 script/Average_Nucleotide_Identity/readme.txt 파일에서도 이 GitHub를 인용하고 있으니 내 예상이 틀리지는 않을 것이다. ANI.pl이 호출되는 순서는 다음과 같다.

fDIBAC.pl(main script) -> OrthoANI.all_tre.new.py -> orthoAni.sh라는 스크립트를 실행 단계에 작성하여 활용함

OrthoANI.all_tre.new.py에서 orthoAni.sh 파일을 작성하기 위하여 다음과 같은 문자열을 만들어 ANI.pl을 실행하도록 만드는데, 이게 또 이상하다. 


ANI.pl의 필수 옵션인 '-fd formatdb -bl blastall'이 빠진 상태이다.  이 옵션은 formatdb와 blastall 실행 파일을 지정하기 위한 것이다. $PATH에 위치한다고 하여 생략해서는 안 된다. 따라서 그림에서 보인 cmd로는 ANI.pl이 제대로 실행되지 않는다. 이것이 두 번째 문제이다. 그러면 cmd를 조합하는 명령어 라인에 '-fd formatdb -bl blastall'을 삽입하면 되지 않을까 생각할 수 있다. 그러나 전혀 그렇지 않다. ANI.pl은 출력 파일을 쓰지도 않고, 오로지 표준 출력에 두 genome으로부터 계산한 ANI 수치를 표시할 뿐이다 OrthoANI.all_tre.new.py 스크립트의 후반부를 보면 ANI 수치 쌍을 전부 조합하여 하나의 OrthoANI.txt 파일을 만드는 것으로 되어 있는데, 내가 알고 있는 ANI.pl의 출력 형식으로는 이를 어떻게 만드는지 이해하기 어렵다. 이것이 세 번째의 문제이다.

full_path_to_file_1 VS full_path_to_file_2
 ANI: 93.0621988037596

세 번째 문제의 해결을 위해서 지금까지 미루어 두었던 파이썬 공부를 시작할 용의가 있다. 지금이 아니라면, 앞으로 영원히 파이썬 문맹으로 남게 될지도 모른다.

2021년 진공관 앰프 제작을 위한 정보 수집

세상은 넓고 고수는 많다. 인터넷을 뒤져서 단편적으로 얻은 지식에 내 생각을 약간 달아서 블로그에 적는다고 하여 세상의 지식이 늘어나는데 보탬이 될 일은 없을 것이다. 그저 내 기억력을 보조하기 위한 수단으로 기록을 할 뿐이다.

지금까지 예닐곱대의 진공관 앰프를 다루어 보았다. 여기에는 외부에 제작을 의뢰한 것, 알리익스프레스에서 완제품으로 구입한 프리/헤드폰 앰프가 전부 포함된다. 완성품 보드를 구입하여 주변 부품만 붙인 것도 있고, 빈 PCB만 구해서 부품을 손수 납땜하여 만든 것도 있으며(6LQ8 PP & SE), CAD를 이용한 상판 설계와 point-to-point wiring으로 전 과정을 만든 것도 있다. 매우 낮은 출력이지만 푸시풀 앰프도 있었다.

내년에는 '제대로 된' 푸시풀 앰프를 point-to-point 결선 방법으로 만들고 싶다. 기왕이면 고정 바이어스 회로를 채택하여 서로 매칭이 되지 않은 출력관이라 하더라도 무난하게 쓰도록 만들자는 목표를 삼아 보았다. 아직 출력관은 정하지 않았다. 갖고 있는 PCL86을 그대로 쓸 수도 있고, EL84나 6V6도 고려 대상에 들어간다. EL84 또는 6V6은 완제품 혹은 반제품 형태의 PCB가 알리익스프레스 등에 흔하게 팔린다. 이걸 그대로 사서 쓴다면 만드는 재미는 반감될 것이고, 바이어스 조정도 힘들어진다.

푸시풀 앰프를 만드는데 필수적인 요소인 위상분리기(phase splitter) 또는 위상반전기(phase inverter)의 작동 원리를 이해하는 것도 어렵지만, 요즘은 고정 바이어스 공급 회로의 오묘함에 더욱 매력을 느끼는 중이다.

필요한 정보를 찾아서 목록으로 만들어 보았다. 특히 세상에 없던 6LQ8 사용 오디오 앰플리파이어가 자작인들을 통해서 2010년대 중반에 등장하게 되었으므로 그 개발 뒷이야기를 알아두는 것이 유익할 것이다.

[제이앨범] 작업실 > 앰프 자작 > 11LQ8/6LQ8 카테고리의 모든 글

[6LQ8 PP 관련 회로도 자료]  

[다음 블로그: 끝없는 여정] 6V6 푸시풀 파워앰프 - 회로도

[소리전자] PCL86 PP ('시나브로') PCB형 인티앰프 일반형 부품키트 - 회로도 참고용. 어떤 타입의 위상분리회로를 쓴 것인지 상당히 헷갈린다.

[Franz Wichlas] 4 x PCL86-PP - Amp - Jogis Röhrenbude의 웹사이트에 게시됨

[DIY Audio] New build of P-P PCL86 Amp - 로그인 필요. 회로도는 맨 아래 포스팅 참조

[DIY Audio] PC86 - Worth a try?

[The Tube CAD Journal] Phase splitters

[The Bona's HomePage] Phase Splitter - 매우 간결하게 설명을 잘 하였다. 이에 의하면 제이앨범이 디자인한 6LQ8 PP 앰프는 cathodyne phase splitter를 사용한 것이다.

[Audio Asylum] ECC802S SRPP / EL84(6BQ5) Push-Pull Amplifier - 25R 가변저항을 이용하여 두 출력관 사이의 바이어스 전류 균형을 맞춘다. 이런 용도로 사용할 가변저항의 와트 수는 어느 정도가 적당할까? 예를 들어 이런 제품의 규격은 0.5W이다. 2W를 견디는 다회전 볼륨은 훨씬 비싸다.

[Atra-Audio] PP Fixed Bias circuit design and calculator

[The Valve Wizard] Bias Supplies

[VTADIY: Vacuum Tube Amplifiers DIY] 5.3 Power supply for the fixed grid bias of a vacuum tube amplifier

2021년 11월 23일 화요일

fIDBAC: A Platform for Fast Bacterial Genome Identification and Typing 공부하기

유전체 서열 정보를 이용한 미생물 균주의 정확한 동정은 기초 생명과학과 감염병 관련 연구 분야에서 매우 중요한 일이다. 종(species) 또는 더 세분화된 수준으로 균주를 동정하거나 - 후자를 보통 타이핑(typing)이라고들 한다 - 유전체가 보유한 항생제 내성(AMR, antimicrobial resistance) 및 병원성 인자(VF, virulence factor)를 예측하는 것도 중요하다. 만약 유전체 해독을 실시한 미생물 균주가 쓸모있는 이차대사물을 만들어내는 것 같다면, 이를 책임지는 유전자 클러스터를 예측하는 것도 필요하다.

이러한 분석을 한꺼번에 실시하는 '입맛에 딱 맞는' 파이프라인은 아직 눈에 잘 뜨이지 않는다. 다만 병원성 미생물의 동정과 관련된 도구들은 가끔씩 눈에 보여서 이를 설치하고 테스트하면서 나름대로 평가를 하고 있다.

Frontiers in Microniology에 최근 공개된 fIDBAC라는 분석 플랫폼을 요즘 열심히 테스트하고 있다. fIDBAC는 면밀한 검토를 거쳐서 작성된 type strain 유전체 DB(N=12,784; 전체 목록은 12784.genome.txt를 참조)을 바탕으로 하여 미지의 유전체가 주어졌을 때 이것이 어떤 종에 속하는지를 정확히 판별해 주는 것을 목표로 한다.

다음 그림은 논문에서 제공한 graphical abstract이다.
fIDBAC의 프레임워크. 출처 링크
fIDBAC 개발에서 가장 심혈을 기울인 것은 type strain의 유전체 DB curation이었을 것이다. 논문을 보면 NCBI에서 균주의 source에 대한 정보를 참고하여 유전체 정보를 수집한 뒤, LPSN과 Bergey's Manual 및 IJSEM 논문을 참고하여 입증된 종명 및 type strain 정보를 완성했다고 한다. 이명(synonym) 정보를 정리하는 수고스러운 작업도 포함하였다. 

여기까지는 이름이 제대로 되어 있는지를 점검하는 것에 지나지 않는다. 다음 단계에서는 해당 균주의 유전체가 얼마나 온전한지를 확인하는 것이 필요하다. CheckM으로 점검하여 contamination이 5%를 넘거나 completeness가 90% 미만인 것은 버리고, 16S rRNA sequence를 추출하여 LTP database와 비교하여 genus level에서 불일치하는 것은 제외하였다.  

SILVA, RDP, GreenGenes DB는 많이 들어 보았지만 LTP('The All-Species Living Tree' Project)는 생소하여 검색을 해 보았다. 이곳에서 LTP의 상세한 연혁 소개와 함께 다운로드 기능도 제공한다. 앞서 소개한 잘 알려진 ribosomal RNA database가 있음에도 불구하고 LTP가 별도의 프로젝트로서 존재해야만 했던 이유가 있었을 것이다.

큐레이션의 마지막 단계에서는 모든 유전체에 대한 pairwise ANI 계산을 실시하여 동일한 종명을 갖는 것끼리를 클러스터링하였다. 여기에서는 SciPy 파이썬 패키지를 사용하였다고 한다. 이렇게 만들어진 type strain의 유전체 염기서열은 KmerFinder를 통해서 k-mer (다운로드 링크)DB로 변환하였다. KmerFinder에 대한 상세한 정보는 관련 논문을 참조하라.

Reference DB 구축이 완료되었으니, 이를 활용하여 query genome을 동정하는 방법을 수립하는 것이 fIDBAC platform의 나머지 절반 과정이 되겠다. 메인 스크립트(fIDBAC.pl)을 통해서 작동 순서를 알아보면 다음과 같다. 이는 논문에서 소개한 순서와는 조금 다르다.

  1. Reference genome의 k-mer DB에 대하여 검색 실시(findTemplate without -w). 결과 파일은 $sample.out.txt이다. 
  2. Genome annotation 실시(prokka) 후 16S rRNA와 유전자 서열 추출. 아미노산으로 번역한 유전자는 항생제 내성 및 병원성 인자를 탐색하는데 쓰임(AR_VF.run.pl)
  3. 16S rRNA에 대한 검색 및 fastANI를 이용하여 query genome의 종 동정 정보를 fastANISpecies라는 파일에 한 줄로 출력. 이것은 select_kmerfinder_16SAndANI.pl 스크립트를 통해서 이루어지는데 내부 구조가 좀 난해하다. [1]번 과정의 결과 파일($sample.out.txt)로부터 top1/3/10/20의 목록을 취한다. 최고 스코어를 보이는 genome이 3개라면 top1 목록은 세 줄이 된다. FASTA file 이름으로부터 목록 파일을 참조하여 taxonomic name을 얻는 과정이 꽤 된다. 16S rRNA 서열은 16S.V3.fa(관련 파일은 전부 여기에 있음; 이것이 LTP3132_SSU에서 유래한 것으로 추정됨)에 대하여 blastn을 실행하여 top 20을 얻은 뒤, local FASTA file에서 hit에 해당하는 것을 찾아 놓는다. fastANI에 계산에 투입되는 top 20은 16S 분석과 KmerFinder에서 얻어진 top genome을 추려서 얻어지는 것이 아닐까 생각하는데, Perl 스크립트를 뜯어보면서 정말 그러한지를 확인하지는 못하였다. 논문에서는 "The top 20 closest species were extracted from the results of the above-mentioned methods(즉 16S rRNA 서열 분석과 k-mer 분석)."이라고 하였으니 두 가지 결과를 종합하는 것이 맞을 것이다. 단, 두 가지 분석을 통해 얻어진 top 20 closest genome 안에서 종명이 consistent하지 않은 것이 섞여있을 경우 어떻게 처리하는지는 아직 잘 모르겠다.
  4. OrthoANI.all_tre.new.py 스크립트를 사용하여 ANI.pl 실행. 이 Perl wrapper script는 엄밀히 말해서 천랩이 개발한 OrthoANI와는 다른 것인데 왜 이런 이름으로 부르는지를 모르겠다. 이 wrapper script는 아직 잘 작동하지 않는 것 같다. 왜냐하면 주요 결과 파일인 OrthoANI.txtd와 ANI.all.txt가 빈 상태이기 때문이다. 왜 그런가 추적을 해 본 결과 이유는 너무나 허무한 곳에 있었다. OrthoANI.all_tre.new.py 스크립트 내부에 ANI.pl의 경로가 지정되어 있었다. 아니, config_db.txt 파일은 뭐하러 만들었단 말인가? 부글부글...
  5. ANIcaculator를 사용하여 gANI 계산. 유전자는 prokka가 만든 것을 이용한다. Step [4]와 [5]의 상호 연관성은 아직 파악하지 못하였다. ANI > 95%인 것만을 선별하는 것은 fastANI 레벨인가, gANI 레벨인가?
  6. MLST analysis
  7. SNP and MST analysis
  8. ....어휴!
스크립트를 뜯어보면서 작동 순서를 정리하려다가 머리에 쥐가 날 지경이 되었다. 이 목록은 앞으로 시간이 나는대로 틈틈이 업데이트를 해 보겠다. fIDBAC을 로컬 머신에 설치하면서 약 이틀 이상을 삽질(?)한 것을 생각하면 저절로 주먹에 불끈 힘이 솟는다. 설명은 너무나 부실하고, 필수 스크립트가 GitHub 사이트에 빠져 있고, 일반적인 tab-delimited file을 .xls 파일이라 부르는 등 사용자로서는 다소 과격한 의견이 되겠지만 프로그램 배포의 기본을 갖추고 있지 못하기 때문이다. MSTgold(MST for 'minimum spanning trees') 프로그램도 설치해야 fIBAC이 돌아가는데, 전혀 설명이 없다. 

아주 작은 사례를 보자. 사용자가 직접 수정해야 하는 config_db.txt 파일을 제공하고 있기 때문에 이것만 고치면 fIDBAC.pl 스크립트가 돌아갈 것으로 착각하기 쉽다. 그런데 엉뚱하게도 개별적인 스크립트 안에는 필수 프로그램들이 다른 경로로 지정되어 있는 것이 아닌가. 그것도 개발자 환경의 full path로 말이다. 테스트 러닝을 하면서 실행이 안되는 프로그램을 찾아 스크립트 내부를 수정하고, 또 실행 후 수정을 반복하고... 몇 차례의 시행 착오를 거쳐서 최초의 결과물을 손에 쥘 수 있었다. 그리고 본 블로그에서 자세히 설명하지는 않았지만, fIDBAC 개발자가 제시한 12,784 유전체 염기서열을 GenBank에서 다운로드하는 것도 다소 번거로왔다. 개발자는 accession number만 제시했을 뿐, 분석 과정에 필요한 유전체 및 유전자 염기서열을 따로 준비하여 목록 파일을 만들어 두어야 한다. 목록 파일(샘플)을 만드는 설명도 부실해서 애를 먹었고, 이들이 제시한 유전체 중 이미 수십 개는 GenBank에서 탈락하거나 업데이트된 것이 있어서 이를 다시 찾아야 한다. Genome assembly의 업데이트는 GCA_#########. 1뒤의 숫자가 하나 증가하는 것(1 -> 2)으로 표현되는 것도 있으나. 어떤 것은 아예 다른 accession no.로 대체되는 것도 있어서 이를 파악하려면 수작업에 의존하지 않을 수가 없었다.

이렇게 불평을 하기에 앞서서 과연 나는 다른 연구자들에게 도움이 될 수준의 완성도를 갖춘 스크립트를 공개한 적이 있었나? 블로그를 통해서 팁 수준의 짤막한 코드 조각을 이따금씩 올렸을 뿐이다. 남을 비판하기에 앞서서 나부터 반성을 하자.

논문으로서는 좋은 설계 개념을 제시하였지만 프로그램을 가져다 쓰려는 사람에게는 불편한 것이 사실이다. 그런 사람을 위하여 웹사이트를 구축해 놓았으니 꼭 필요한 사람은 이를 사용하면 된다. 앞으로 신종 박테리아는 계속 나올 것이고, 이를 얼마나 충실하게 업데이트를 할지는 지켜보아야 할 일이다. 업데이트는커녕 2년 정도 유지되다가 슬쩍 사라지는 웹사이트도 많이 있기 때문이다.

치명적인 약점(?) 또는 개선할 점을 생각해 보았다. 우선 archaea는 레퍼런스 DB에 전혀 반영되어 있지 않다는 점을 들고 싶다. Archaea는 인체에 병을 일으키는 것이 없다고는 하지만, 일반적인 species 동정을 목적으로 fIDBAC을 이용하려는 사람에게는 치명적인 단점이다. 만약 고균을 포함한다면 fIDBAC은 fIDBAAC(...Bacterial and Archaeal...)이 되어야 할 것이다. k-mer DB는 보다 현대적인 Mash 기반 DB로 바꾸는 것이 바람직할 것이다. 12,000개 정도의 유전체 서열을 다운로드하여 k-mer DB(maketemplatedb 명령)을 어젯밤부터 실행했는데 아직 400개도 진행을 하지 못했다. 파이썬 2.7을 써야 하고(버전 3에서 작동하도록 고쳤다는데 잘 안됨), 다중 쓰레드도 쓰지 못하니 너무나 답답하다.

그래도 fIDBAC을 뜯어보면서 얻은 성과가 있다면 ReadSeq이나 phylip 등 고전적인 프로그램을 다룰 기회가 생겼다는 점이다. 








2021년 11월 17일 수요일

43 오극관 싱글 앰프의 개선 작업 마무리

전원 트랜스포머의 교체 및 B+ 전압 조정 작업이 얼추 끝났다고 생각하고 음악을 듣는데, 좌우 채널 전체에서 '버석 버석'하는 소리가 들리기 시작했다. 접촉 불량인가, 새로운 타입의 발진인가? 또다시 좌절감에 휩싸였다. 겨우 쌍삼극관(6N2P) 하나와 핀이 6개 달린 구형 power pentode 두 개를 가지고  점대점 결선(point-to-point wiring, 나는 하드 와이어링이라는 표현이 옳지 않다고 믿음)으로 만든 싱글 앰프 하나를 제대로 못 만들다니! 내가 X손이라니!

앰프를 뒤집어 놓고 이곳 저곳을 건드려 보았다. B+ 전원 공급을 위해 새로 만들어 넣은 기판을 움직일 때 이에 맞추어 잡음도 같이 발생하는 것을 눈치챘다. 만능기판에 납땜한 스크류 터미널의 접촉이 아무래도 문제인 것 같았다. 나사를 풀고 기판의 동박에 납땜으로 직결을 하였더니 잡음이 사라졌다.

차폐용 커버가 없는 일반(오디오용이 아닌) 전원 트랜스포머를 쓰게 되면서 전원부에서 유도되는 잡음이 이전보다는 약간 늘어난 것 같았다. 리플 제거 보드를 사용함에도 불구하고 험이 들린다는 것은 몹시 자존심이 상하는 일이다. 전원을 넣으면 전원 트랜스 자체에서도 미약하나마 울리는 소리가 난다. 아마도 유도 잡음을 유발하는 부품의 배치 문제일 것으로 여겨진다. 실용상으로는 별 문제가 없으므로 당장은 그냥 쓰기로 하였다.

잡음의 원인을 찾는답시고 출력관을 좀더 상태가 좋은 실바니아 것으로 바꾸어 끼웠다.
대단한 것을 이룬 것은 아니지만 진공관 앰프의 기본에 대하여 이해도를 높인 좋은 계기가 되었다고 생각한다. 아직도 앰프를 뒤집어 놓고 보면 손을 대고 싶은 곳이 한두 군데가 아니다. 하루 아침에 해결될 문제는 아니다. 빨리 끝내려고 할 일이 아니라 과정을 즐기는 것, 그것이 진정한 아마추어의 자세이자 특권이 아닌가 싶다. 

아마추어는 과정을 즐기고, 프로는 결과로 말한다.

2021년 11월 16일 화요일

Roary 'query_pan_genome'의 고약한 출력물

Roary: the Pan Genome Pipeline은 내가 밥 먹듯이 즐겨 사용하는 프로그램이다. 최신판은 2019년 11월 6일에 릴리즈되었으니 더 이상 개선의 여지도 없는 프로그램이 아닐까 한다. MUMmer와 같이 거의 수정도 거치지 않으면서 오랜 기간 사랑받는.

Roary의 부속 유틸리티인 query_pan_genome은 두 그룹 간의 교집합, 합집합, 또는 차집합에 해당하는 gene cluster를 추출하는 다재다능한 프로그램이다. 그룹 정보는 GFF 파일로 제공하면 된다. 예를 들어 그룹 one에는 있지만 그룹 two에는 없는 유전자를 찾고 싶다면, 다음과 같이 실행하면 된다.

$ query_pan_genome -a difference --input_set_one 1.gff,2.gff --input_set_two 3.gff,4.gff,5.gff

만약 각 그룹을 구성하는 GFF 파일이 많다면 명령행에 이를 일일이 타이핑하기가 곤란할 것이다. 그런 경우에는 fofn(file of file name)을 만든 뒤 paste 명령을 써서 쉼표를 사이에 두고 이어 붙이면 된다.

$ cat one.fofn
1.gff
2.gff
3.gff
$ one=$(paste -sd, one.fofn)

두 그룹에 대하여 이렇게 변수를 만든 다음, '--input_set_one $one'과 같은 형식으로 옵션을 주면 된다. GFF 파일이 현재 디렉토리에 없다면, fofn을 만들 때 find 명령어를 잘 이용하면 된다.

'query_pan_genome -a difference' 명령을 수행하면 'set_difference_'로 시작하는 11개의 파일이 생긴다. 그룹 one에만 있는 유전자에 대한 정보는 다음의 파일에 수록된다.

  • set_difference_unique_set_one
  • set_difference_unique_set_one_reannotated
  • set_difference_unique_set_one_statistics.csv
여기서 한 가지 주의할 것이 있다.  그룹 one에만 있는('unique') 유전자란, 그룹 one의 모든 멤버에 다 있는 것만을 포함하는 것이 아니다. 예를 들어 그룹 one이 10개의 균주(A, B, C ... J)로 이루어졌다고 하자. 그러면 set_difference_unique_set_one에는 A에만 있는 것, A와 B에 있는 것 등 별의별 녀석들이 다 섞여 있다. 만약 그룹 특이적인 유전자를 찾는 것이 목적이라면, A부터 J까지 모든 멤버에 다 있는 것을 파악해야 한다. 그러려면 위에서 나열한 세 개의 결과 파일 중 set_difference_unique_set_one_statistics.csv를 참조해야 한다.

여기에서 문제가 발생한다. 바로 gene ID가 바뀐다는 것이다. set_difference_unique_set_one의 첫 번째 컬럼은 Roary 실행 결과 파일에서 보편적으로 쓰이는 unique cluster ID를 유지하지만(cluster는 accessory gene도 포함, 즉 pan genome을 전부 아우름), set_difference_unique_set_one_reannotated에서는 GFF 파일을 참조하여 일부가 gene name으로 바뀌고, 이것이 set_difference_unique_set_one_statistics.csv로 그대로 전달된다.

실제 사례를 들어 보자. 오늘 아침에 query_pan_genome 스크립트로 Strepcococcus genus에 속하는 두 종(N=301, set one은 232개, set two는 69개)의 roary 결과를 투입하여 13683개의 set one-specific gene을 얻었다. 그런데 gene ID를 sort하여 uniq 명령어를 통과시키면 13553개만 남는다. reannotated gene ID에서 중복이 발생했다는 뜻이다.

set_difference_unique_set_one과 set_difference_unique_set_one_reannotated 파일의 나머지 컬럼에는 실제 이를 구성하는 유전자들의 ID가 있으니 그룹 자체를 건드리지는 않는다. 그러나 pan_genome_reference.fa 파일에서 set one에 특이적인 유전자(대표 서열로서)를 찾으려면 여간 번거로운 일이 아니다.

query_pan_genome 스크립트를 전혀 쓰지 않고 roary의 기본 결과 파일인 gene_presence_absence.csv를 R에서 조작하여 각 그룹 특이적인 유전자를 찾을 수는 있는데 여간 성가신 일이 아니다. 왜 query_pan_genome 스크립트는 다시 annotation을 하여 식별자를 유일하지 않은 것으로 만들어 버리는지 알 수가 없다. QueryRoary.pm를 내가 고칠 수 있는 수준의 일도 아니고...


2021년 11월 11일 목요일

43 싱글 앰프 튜닝 - 전원부 개선

전원 트랜스포머를 220V 50W 절연트랜스로 교체하고, 초단관을 12DT8에서 원래 구성이었던 6N2P로 바꾸었다. 그리고 각 진공관은 최적 B+ 전원 조건에서 구동하고자 실험을 시작하였다. 일반적인 진공관 앰프에서는 전원장치에서 출력트랜스포머를 우선적으로 연결하고, 저항으로 전압 강하를 하여 초단관의 플레이트로 보내게 된다. 그런데 구식 라디오에 쓰이던 43이라는 오극관은 요구하는 플레이트 전압이 매우 낮다. 최대 160V, 찌그러짐이 가장 적은 조건(2와트 출력)에서는 135V이다. 콘트롤 그리드에 걸리는 바이어스 전압 -15V를 감안허면 155V가 가장 적당한 수준의 전압이 된다. 그러나 드라이브단의 6N2P의 플레이트 전압은 ~250V 정도가 되어야 한다.

인터넷에 43 오극관의 데이터시트가 있다는 것이 얼마나 고마운 일인지 모르겠다. 작성일은 무려 1936년 6월 26일다.

지금까지는 43에 공급한 전압을 약간 낮추어서 6N2P에 공급하는 방식이었다. 이렇게 낮은 플레이트 전압에서 6N2P가 작동을 하지 않는 것은 아니지만, 아무리 들어 봐도 소리가 작은 것은 초단관의 동작 상태가 최적이 아닌 것에 그 원인이 있는 것 같아서 개선 작업에 착수하였다. 별 것 있겠는가? 전압을 거는 순서를 바꾸면 되지 않는가. 특히 이번 개선 작업에서는 리플 제거 보드에서 각 진공관으로 연결되는 전압 강하 회로를 채널별로 따로 만들어 보았다. 비교적 값이 비싼 국산 전해 캐패시터가 많이 들어가서 아깝다는 생각이 들었다. 테스트가 끝난 다음에 기판에서 떼어내어 재활용하기도 어려운데...

보유한 저항의 값이 다양하지 않아서 다소 이상한 모습의 회로가 되었다. 리플 제거 회로의 출력은 260V 정도이다. 2.2K옴 저항을 거치면 6N2P 부하저항에 200V 정도가 걸리게 된다. 여기까지는 아주 마음에 드는데, 43 오극관에 맞게 전압을 더 낮추는 것이 문제이다. 6.8K옴 시멘트 저항 3개를 병렬로 연결하여 2.267K옴을 만드니 드디어 120V 정도가 된다. 이보다는 약간 높은 값을 얻어야 하지만 더 이상 병렬로 이어붙일 저항이 없다.

저항의 발열을 생각하면 말이 안되는 테스트 회로이다. 2.2K옴은 겨우 2와트급이다. 3개를 병렬로 연결한 6.8K옴 시멘트 저항(각 5와트)도 매우 뜨거운데, 2와트 금속피막저항 하나가 펄펄 끓는 것은 당연하다. 대략 계산하면 2.2K옴 저항에서 약 2.2W, 시멘트 저항 3개를 병렬로 연결한 합성저항에서는 1.95W가 걸린다.  저항이 뜨거워지면, 여기에 연결된 전해 캐패시터도 덩달아 뜨거워진다. 이래서는 곤란하다. 

2.2K옴 + 1K옴 정도로 2단 구성을 하면 최종적으로 150V 가량을 뽑을 수는 있을 것이다. 문제는 적당한 저항이 없다는 것. 소리는 과거보다 더 커진 것이 확실하다.

처음부터 2차에 150V 정도 출력되는 전원 트랜스포머를 썼더라면 높은 전압을 내리기 위해서 덜 고생을 했을 것이다. 되도록 기성품으로 팔리는 일반 용도의 트랜스포머를 쓰려는 것이 취지였으니, 이런 고생을 해도 감내해야 한다.

이번 개선 작업을 통해 떼어낸 전원트랜스포머(2차 230V 120mA, 6.3V 1.6A)는 정말 무용지물이다. 진공관 앰프를 자작하면서 처음으로 주문 제작을 한 것이었는데, 전기만 통하면 정격을 넘는 것도 아닌데 요란하게 울어대니 도대체 오디오 앰프로 쓸 수가 없는 것이었다. 그래서 전단에 110V 강압트랜스(단권 트랜스라서 용량에 비해 크기는 작음)를 달아서 출력 전압을 낮게 만들어 43 앰프에 사용하였던 것이다. 겉으로 보면 알 수가 없지만, 앰프를 뒤집어 놓으면 아주 가관이다. 
전기만 흘리면 우는 전원 트랜스포머. 그림 출처는 내 블로그의 글이다(링크).
새 앰프를 만들어 보려고 알리익스프레스를 며칠 동안 뒤지다가 기존의 앰프를 개선하는 것부터 먼저 해결하기로 마음을 바꾸어 먹기를 참 잘 한 일이라고 생각한다. 이렇게 단순하고도 아름다운(!) 앰프의 기본적인 문제도 해결하지 못한 상태로 또 다른 앰프를 만든다는 것은 올바른 배움의 자세가 아니다.


2021년 11월 10일 수요일

Bactopia test - 방황의 끝(3): 공식 매뉴얼이나 도움말에서 쉽게 파악하기 힘들었던 기본 개념과 사용법 정리

Bactopia test - 방황의 끝(2) - 2021년 5월 23일 작성

이 주제와 관련하여 '방황의 끝' 혹은 '방황의 끝(1)'이란 제목을 붙인 글은 보이지 않는다. 끝나기 전까지는 끝이 난 것이 아니다! 여전히 bactopia의 공식 문서를 찾아 읽으면서 내가 뭘 잘못 이해하고 있었는지를 깨닫고 있다. 몇몇 데이터셋은 self-signed SSL certificate의 문제로 집에서 다운로드한 뒤 직장으로 가지고 나와서 복사를 하는 번거로운 과정을 거쳐야 하지만, 활용 빈도가 늘어나면서 점차 익숙해지는 중이다.

Bactopia를 설치한 뒤 가장 먼저 할 일은 데이터셋을 빌드하는 일이다. 쓰이는 명령어는 'bactopia datasets'이다. 우선 다음의 것은 species를 확정하지 않은 상태('--species STR' 파라미터를 제공하지 않음)에서 설치되는 데이터셋이다. 설치 가능한 모든 데이터셋이 궁금하면 'bactopia datasets --available_datasets' 명령어를 실행하면 된다.

  • ariba: 기본 세트는 vfdb_core와 card
  • minmer
  • amr: NCBI의 AMRFinder+ database
Species를 확정하면 prokkamlst dataset를 더 가져온다.  Prokka dataset은 유전체 주석화를 위해 미리 준비하는 단백질 서열 자료이다. RefSeq에서 complete genome('--assembly_level' 파라미터를 써서 다른 것으로 조정 가능)에 대한 GenBank 파일(max 1000; '--limit' 파라미터로 조정 가능)을 받아서 단백질 서열을 추출한 뒤 cd-hit로 클러스터링하여 이를 마련한다. '--include_genus' 파라미터를 주면 지정한 species외에도 이것이 속하는 genus의 유전체 정보를 더 가져와서 단백질 자료를 만든다. RefSeq에서 다운로드한 원본 파일을 나중에 재사용하려면 '--keep_files' 파라미터를 주어라. 아마 한번에 내려받은 GenBank flat file은 나중에 재사용을 할 일이 무척 많을 것이다. 내가 요즘 bactopia를 즐겨 사용하는 것도 바로 이 기능 때문이다. 이를 위해 뒷단에서 수고를 하는 스크립트는 바로 ncbi-genome-download이다.

이때 다운로드한 GenBank 파일은 prokka 데이터셋을 만들고 끝나는 것이 아니다. 다음의 위치에 Mash sketch DB를 만들어서 실제 유전체 해독 데이터를 이용한 후속 분석 때에 가장 가까운 reference genome을 자동으로 찾는 용도로 쓰인다. 이 reference 서열은 SNP 분석을 위해 필요하다. Reference genome을 자동으로 찾는 것이 싫다면 명령행에서 별도로 지정해도 된다.

datasets/species-specific/SPECIES/minmer/refseq-genomes.msh

datasets 디렉토리에 이미 파일이 있다면, 'bactopia datasets'은 데이터셋 다운로드를 다시 시도하지 않는다. 단, amr 데이터셋은 항상 새로 다운로드하여 설치를 하는 것이 기본 동작이다. 따라서 이것이 성가시다면 '--skip_amr' 파라미터를 주면 된다. 이미 데이터셋이 설치되어 있음에도 불구하고 새로 다운로드하여 설치하려면 '--force' 파라미터를 이용한다.

위에서 굵은 글씨로 표시한 다섯 가지의 데이터셋(ariba, minmer, amr, prokka 및 mlst) 각각에 대하여 설치 생략 혹은 강제 설치를 하려면 각각 --skip_STR 또는 --force_STR 파라미터를 지정하면 된다. STR에는 데이터셋을 나타내는 문자열을 넣어라.

다음은 노트북 컴퓨터에서 Streptococcus pneumoniae의 데이터셋을 빌드하는 과정을 보인 것이다. general dataset이 이미 설치된 상태에서 종을 지정하여 species-specific dataset을 추가로 설치하였다. 

$ bactopia datasets --species "Streptococcus pneumoniae" --include_genus --cpus 4 --keep_files
2021-11-09 21:06:11:root:INFO - Streptococcus pneumoniae verified in ENA Taxonomy database
2021-11-09 21:06:11:root:INFO - Setting up Ariba datasets
2021-11-09 21:06:11:root:INFO - vfdb_core (./datasets/ariba/vfdb_core) exists, skipping
2021-11-09 21:06:11:root:INFO - card (./datasets/ariba/card) exists, skipping
2021-11-09 21:06:11:root:INFO - Setting up pre-computed Genbank/Refseq minmer datasets
2021-11-09 21:06:11:root:INFO - ./datasets/minmer/genbank-k21.json.gz exists, skipping
2021-11-09 21:06:11:root:INFO - ./datasets/minmer/genbank-k31.json.gz exists, skipping
2021-11-09 21:06:11:root:INFO - ./datasets/minmer/genbank-k51.json.gz exists, skipping
2021-11-09 21:06:11:root:INFO - ./datasets/minmer/refseq-k21-s1000.msh exists, skipping
2021-11-09 21:06:11:root:INFO - Setting up antimicrobial resistance datasets
2021-11-09 21:06:11:root:INFO - Setting up latest AMRFinder+ database
2021-11-09 21:06:55:root:INFO - AMRFinder+ database saved to ./datasets/antimicrobial-resistance/amrfinderdb.tar.gz
2021-11-09 21:06:55:root:INFO - Setting up MLST datasets
2021-11-09 21:06:55:root:INFO - Setting up default MLST schema for Streptococcus pneumoniae
2021-11-09 21:06:55:root:INFO - Creating Ariba MLST dataset
2021-11-09 21:07:59:root:INFO - Creating BLAST MLST dataset
2021-11-09 21:08:09:root:INFO - Setting up custom Prokka proteins
2021-11-09 21:08:09:root:INFO - Setting up custom Prokka proteins for Streptococcus pneumoniae
2021-11-09 21:08:09:root:INFO - Downloading genomes (assembly level: complete)
2021-11-09 21:08:10:root:INFO - There are less available genomes than the given limit (1000), downloading all.
2021-11-09 23:15:53:root:INFO - Processing 827 Genbank files
2021-11-09 23:20:11:root:INFO - Median genome size: 2111425 (n=88)
2021-11-09 23:20:11:root:INFO - Running CD-HIT on 1516278 proteins
2021-11-09 23:29:50:root:INFO - Writing summary of available datasets

827개의 유전체 파일을 다운로드하였지만 이는 '--include_genus' 옵션에 의해 다른 종의 것까지 포함하고 있다. 실제로 이 종의 유전체 크기를 추정하는데 쓰인 것은 88개이다. 

2021년 11월 9일 화요일

진공관 앰프 자작용 SMPS가 점점 많이 팔리고 있다

'세계의 공장' 중국이 글로벌 경제에 미치는 영향은 실로 엄청나다. 당장은 싼 값에 공산품을 살 수가 있으니 나처럼 취미로 전자 공작을 하는 사람은 알리익스프레스를 뒤져서 맘에 드는 물건을 싸고 편하게 구입하게 된다. 창의력을 발휘한 물건도 꽤 많아서 때로는 감탄을 하기도 한다. 요즘 어떤 물건이 널리 팔리는지를 보면서 시장이 바뀌어 나가는 모습을 관찰하는 것도 흥미로운 일이다. 

무역의 중국 의존도가 높아지면 다른 문제도 생겨난다. 요즘 겪고 있는 요소수 대란처럼 말이다. 중국의 내부 사정에 의하여 산업과 생활에 꼭 필요한 물건을 갑자기 공급받지 못하면 이렇게 패닉에 가까운 상황이 발생한다. 중국과 지리적으로 너무나 가까와서 본토에서 날아드는 공해 물질에 의한 피해도 무시하기 곤란하다.  

이처럼 세상이 너무나 많이 얽혀 있어서 미처 예상하지 못했던 남의 나라 문제가 우리의 생계에 직접적으로 영향을 주게 된다. 본래 네트워크가 고도화되면 약간의 변동에도 흔들림이 없이 'robust'한 상태가 되어야 하는 것이 아니던가? 오히려 변동이 더 자주 생기고 이를 예측하기 어려워지는 것만 같다.

어찌되었든 연말을 앞둔 소일거리를 찾기 위하여 알리익스프레스를 뒤지고 있는데, 몇년 전에 비하여 진공관 앰프의 전원부로 쓸 수 있는 완제품 SMPS(switched-mode power supply)가 훨씬 다양해졌다. 아래에 보인 것은 빙산의 일각이다. 빨갛게 테두리를 친 것이 250~300V의 직류 고전압 및 히터용 6.3V를 제공하는 것이다. 출력 전압을 자유로이 조절할 수는 없지만, 이 정도라면 6V6 싱글을 물론 푸시풀 앰프 정도까지는 가뿐하게 구동할 수준이 된다.


2018년에 제이앨범 '고야' 회원께서 완성 상태의 자작 SMPS와 부품을 거저 보내주셔서 직접 SMPS를 만든 일이 있다. 이것으로 6N1 + 6P1 싱글 앰프의 B전원과 히터를 성공적으로 작동하였었다. 당시의 글은 제이앨범에 고스란히 남아있다(링크).

SMPS를 직접 만드는 것은 매우 흥미로운 일이지만, 이를 실용 수준으로 만들려면 조심할 점이 많다. 일단 상용 220V를 직접 정류하는 회로를 잘 꾸며야 한다. 무엇보다도 안전하게! 내가 쓴 회로는 무엇이 문제인지 - 아마도 대용량 캐패시터에 의한 돌입전류? - 퓨즈가 자꾸 끊어졌었다. 그리고 출력부가 단락되든지 했을 때 이를 안전하게 대처할 수 있는 방안이 전혀 마련되어 있지 않다. 부하에 의해서 전압이 과도하게 떨어지면 보상을 하는 회로도 필요할 것이다. 그런데 이런 요구사항을 자꾸 충족시키면 자작이 힘들어진다. 

무엇보다도 자작 SMPS는 소형화하기가 어렵다고 생각한다. 무게는 트랜스포머를 쓰는 고전적인 전원회로에 비해서 훨씬 적게 나가지만, 부품이 비교적 많이 들어가서 작게 꾸미는 것이 쉽지 않다. 무엇보다도 어려웠던 것은, 몇 가지 반도체 부품이 정상 상태인지를 확인하는 것이었다. 

결론적으로 말하자면 자작 SMPS는 실험용 전원으로는 적당하다고 생각한다. 원하는 전압을 만들려면 페라이트 코어에 감은 에나멜선의 권선수를 조절하면 되니까 말이다. 그리고 동작 상태의 확인을 위해 오실로스코프도 있으면 좋을 것이다. 이런, 배보다 배꼽이 더 커진다! 내 오실로스코프는 너무 낡아서 화면이 제대로 보이지 않는다...

만약 앞으로 진공관 앰프를 더 만들게 되면, 거의 틀림없이 시판되는 SMPS를 쓰게 될 것 같다. 내가 SMPS 실험을 처음 시작하던 무렵에는 이렇게 많은 물건이 올라와 있지 않았다. 지금 이렇게 다양한 제품이 있는 것으로 미루어 짐작하건대 수요가 충분한 규모로 성장한 것으로 보인다. SMPS는 진공관 앰프에 써서는 안될 물건이라는 예전의 믿음도 이제는 많이 사라진 것이 아니겠는가? 이것도 혁신이라면 혁신이라 불러도 되겠다.

2021년 11월 8일 월요일

독특한 진공관 앰프를 찾아서

가을이 깊어지면서 진공관 앰프의 따뜻한 불빛을 즐기게 된다. 이미 침실에서 두 대의 앰프(43 및 PCL86 싱글 앰프)가 번갈아 가동 중이고, 약 반년 가량 드라마 촬영용 소품으로 외부에 대여해 준 6LQ8 앰프 두 대도 조만간 집으로 돌아올 것이다. 거실에는 올해 장만한 인터M 레퍼런스 앰프 R150PLUS가 자리를 잡아서 좋은 동반자 역할을 한다. 

자작 진공관 싱글 앰프(single-ended amplifier)의 빈약한 출력과 잡음에 실망하여 부품을 전부 해체하여 거두어 들였다가 또 다시 만들기를 반복하는 이유는 무엇일까? 그것은 바로 만드는 과정이 주는 마약과 같은 즐거움이 있기 때문이 아닐까? 한번씩은 다 거쳐 가는 대중적인 오디오 전용관을 나도 반드시 경험해야 한다는 생각은 별로 없다. 원래 오디오 앰플리파이어용으로 제조되지 않은 진공관을 이용하여 소리를 내 보는 것도 즐거운 일이다. 

적극적으로 이러한 일을 개척해 나가는 사람은 따로 있다. 나는 그들이 남긴 '유산'을 쉽게 이용할 뿐이다. 요즘 알리익스프레스에 들어가 보니 진공관 앰프를 위한 스위칭 파워 서플라이가 예전보다 종류가 많아졌음을 알 수 있다. 이것도 전통과는 거리가 있는, 새로운 시도라고 여겨진다.

갖고 있는 트랜스포머류를 활용할 수 있는 독특한 진공관 앰프 반제품은 뭐가 있을까? 뿔난 항아리, 또는 부항을 닮은 FU-32(Valve Museum의 소개)라는 진공관이 눈에 뜨인다. 1950년대에 초단파(VHF) 송신용으로 쓰이던 832A를 중국에서 오디오용으로 재해석하며 만든 것이라 한다. 빔 테트로드 한 쌍이 하나의 유리관 안에 들어 있어서 저출력의 앰프를 만들기에 매우 적당하고 소리도 나쁘지 않다고 한다. 우선은 이 앰프 보드에 관심을 두고 있다. 아래에 보인 제품에서는 6J1이 드라이브단에 쓰이지만 쌍삼극관인 12AX7이나 6N2를 사용하는 것도 있다.

출처: 알리익스프레스 GHXAMP Worldwide Store
마침 220V 50VA 급의 절연 트랜스포머를 갖고 있어서 전원부를 구성하기에 충분하다. 출력트랜스포머로는 R코어에 직접 감은 것(5K:8)이 한 조 있다. 이전의 6N1 + 6P1 싱글 앰프에서 해체하면서 리드선 부분이 훼손되어 이를 쓰려면 손을 좀 보아야 한다. 소출력 앰프에 쓰기에는 코어 크기가 좀 아깝다.

관심을 두는 두번째의 대상은 6N9P(6SL7GT와 유사)와 6N8P(6SN7GT와 유사)를 사용한 푸시풀 앰프이다. 쌍삼극관을 한 채널의 푸시풀 출력 회로에 이용한다는 것이 독특하다. 내가 잠시 사용하였던 이영건 선생님 제작 6J6도 비슷한 개념의 앰프로 볼 수 있다. 원래 출력관으로 만들어진 것이 아니라서 이 제품운 2.5 W + 2.5 W 정도가 나온다고 한다.

출처: 알리익스프레스(판매자 링크)
보드 안에 정류 및 초크 코일을 이용한 평활회로가 들어 있어서 외부에서 전원트랜스만 연결하면 된다. 이 앰프 보드가 흥미로운 것은 LM7906 레귤레이터를 이용한 히터 점화와 일반 교류 점화를 전부 지원한다는 것이다. 회로도를 보면 출력관에 해당하는 6N8P에 대해서 두 가장 방식 중 하나를 골라 쓰라는 것처럼 보인다. 그러면 앞부분의 6N9P는 어떻게 점화를 한다는 것인가? 설명을 아무리 보아도 잘 이해가 가지 않는다. 혹은 초단(6N9P)에는 레귤레이터를 통한 DC를, 출력단(6N8P)는 AC를 가하여 점화를 한다는 것을 다소 혼동스럽게 기술한 것인지... 내가 보기에는 이것이 더 자연스럽다. 회로도는 아래에 소개하였다.


이 보드에 사용할 수 있는 여분의 푸시풀용 출력 트랜스포머도 갖고 있다. 다 예전에 쓰던 것을 해체하여 마련해 둔 것이다. 

두 제품 전부 가격은 거의 비슷하다. 세일이 시작되는 11월 11일이 되면 적당한 것을 골라서 연말 공작용으로 구입할 생각이다. 둘 다 개성이 넘치는 앰프이니 선택을 하려면 상당히 고심을 해야 될 것 같다.

도커(Docker) 컨테이너를 다른 컴퓨터로 옮겨서 실행하기

직장 전상망 내에서 Bactopia를 사용하면서 가장 불편한 것은 일부 general dataset이 SSL 인증서 문제로 다운로드가 불가하다는 것이다. 소위 SSL 가시성 확보를 위해 설치한 웹프록시 장비에서 발급한 인증서를 인정하지 못하는 상황이 벌어지기 때문이다. 장비 제조업체에서 제공하는 인증서를 웹 브라우저 등 일부 애플리케이션에 설치하여 대부분의 업무는 해결이 가능하지만, 프로그램에 따라서는 매우 까다로운 설정을 요구하는 것이 있다. 우분투나 CentOS 각각에 맞추어 system-wide하게 인증서를 설치하는 방법을 따라서 진행을 해도, 파이썬 코드 안에서 직접 파일을 다운로드하도록 짜 놓은 애플리케이션에서는 한계에 부딛히고 만다. 함수를 직접 건드려서 이 문제를 해결하는 방안이 인터넷에 종종 보이지만, 나는 파이썬을 잘 하지도 못하고 더군다나 고칠 곳이 여러 군데라면 여간 성가신 일이 아니다!

그래서 집에 리눅스 서버를 하나 더 설치해 둔 뒤 여기에서 필요한 파일을 받은 다음 이를 다시 직장에 가져와서 덮어 씌우는 번거로운 노력을 들여야만 했다. 이런 편법을 이용하여 Bactopia를 사용해 왔었다. 

만일 도커(Docker)를 이용하여 Bactopia를 사용한다면, 집 서버에서 이를 어떤 형태로든 export하여 직장 컴퓨터로 가져온 뒤 실행하면 되지 않을까? 생각이 여기까지 미쳐서 이를 실제로 테스트해 보기로 하였다. 

도커를 사용한다면 집에 있는 서버가 실제 직장 서버와 같은 리눅스 배포판을 쓸 이유도 없다. 노트북 컴퓨터를 들고 다니면서 도커 환경을 구축한 뒤 파일 형태로 가져올 수만 있으면 된다. 자, 그러면 시작해 보자. 우분투에 최신 도커 엔진을 설치하는 방법은 공식 문서에 잘 나오니 라를 따라하면 된다.


노트북 컴퓨터(집)에서

$ sudo docker pull bactopia/bactopia 
$ mkdir bactopia
$ cd bactopi
$ sudo docker run --rm -it --workdir /bactopia --volume $(pwd):/bactopia:rw bactopia/bactopia:latest bactopia datasets

도커 컨테이너가 생성·실행되면서 general dataset이 현재 디렉토리에 설치되고, 도커는 곧 종료된다. 현 디렉토리(bactopia) 하위에 만들어진 datasets(2021년 11월 7일 기준으로 1.1 GB)를 그대로 가져다가 직장 컴퓨터에 만들어진 Bactiopia 환경에 그대로 복사를 해도 된다. 실제로 지금까지 해 온 방식과 같다. 단지 도커를 사용하지 않았을 뿐이다.

그러나 이렇게 다운로드한 데이터셋을 한 덩어리의 파일로 전환한다면 다른 곳에 이전하기도 쉽고 교육 등의 목적에 활용하기에도 편리할 것이다. 그러기 위해서는 일단은 도커 컨테이너 형태로 실행 중이어야 하고 파일의 위치도 살짝 바꾸어야 한다. /bactopia 안에 설치된 datasets 디렉토리와 하위 구조들이 컨테이너의 export 결과물에 포함될 것 같지만 내가 테스트해 본 바로는 그렇지 않았다. 현재 상태에서 컨테이너를 다시 기동한 뒤 /bactopia 하위의 자료를 컨테이너 내부의 다른 곳으로 복사해 두어야만 했다. 혹시 내가 잘못 알고 있는 것은 아닌지 모르겠다.

다음과 같이 현재의 작업 위치에서 다시 도커 컨테이너를 생성하되, 편의상 test라는 이름을 붙인다('--name test'). 그리고 /bactopia/datasets을 /datasets으로 복사한다.

$ sudo docker run --rm -it -w /bactopia -v $(pwd):/bactopia:rw --name test bactopia/bactopia:latest
(base) root@6016f6628419:/bactopia# cd ..
(base) root@6016f6628419:/# cp -r bactopia/datasets/ .

이 상태에서 터미널 창을 하나 더 열고 'docker export' 명령을 이용하여 실행 중인 컨테이너를 파일로 저장한다. 

$ sudo docker export test > save.tar

한 덩어리의 파일로 전환하였으므로 어디로든 복사하여 가져갈 수 있다.


실제 작업을 할 서버 컴퓨터(직장)에서

save.tar 파일 다른 컴퓨터로 복사했다 하여도 이를 직접 로드할 수는 없다. 이를 도커 이미지 저장 창고에 밀어 넣은 뒤에 실제로 사용이 가능하다. 이때 사용하는 명령어는 'docker import'이다. 

$ sudo docker import save.tar test
sha256:54f65ae7f5b380804d5b5ffa220774fd5a6c4da90eba1ee47e8554cc498b71e9
$ sudo docker image ls
REPOSITORY                       TAG                    IMAGE ID            CREATED              SIZE
test                             latest                 54f65ae7f5b3        About a minute ago   2.97 GB
hyjeong/ununtu                   v3                     731d875559ed        2 days ago           72.8 MB
hyjeong/ubuntu                   revised                cf60cacf2511        2 days ago           72.8 MB
docker.io/ubuntu                 latest                 ba6acccedd29        3 weeks ago          72.8 MB
docker.io/hello-world            latest                 feb5d9fea6a5        6 weeks ago          13.3 kB
docker.io/centos                 latest                 5d0da3dc9764        7 weeks ago          231 MB
docker.io/ncbi/pgap              2021-07-01.build5508   d84e714fc606        4 months ago         8.43 GB
docker.io/bactopia/bactopia      latest                 20dfd67122b5        5 months ago         1.82 GB
docker.io/antismash/standalone   latest                 c535168192d3        5 months ago         10 GB

특별히 tag를 지정하지 않았다면 'latest'가 붙을 것이다. 이제 컨테이너를 기동해 보자. 간단히 확인만 하기 위하여 복잡한 옵션은 하나도 붙이지 않았다. 단, 이번에는 맨 마지막에 엔트리포인트('bash')를 지정해 주어야 한다.

$ sudo docker run --rm -it test:latest
/usr/bin/docker-current: Error response from daemon: No command specified.
See '/usr/bin/docker-current run --help'.
$ sudo docker run --rm -it test:latest bash
(base) root@451b3e3692f2:/# ls datasets/
antimicrobial-resistance  ariba  minmer  species-specific  summary.json
(base) root@451b3e3692f2:/# ls -l datasets/
total 4
drwxr-xr-x 2 root root   63 Nov  7 23:59 antimicrobial-resistance
drwxr-xr-x 2 root root  102 Nov  7 23:59 ariba
drwxr-xr-x 2 root root  141 Nov  7 23:59 minmer
drwxr-xr-x 3 root root   29 Nov  7 23:59 species-specific
-rw-r--r-- 1 root root 2021 Nov  7 23:59 summary.json
(base) root@451b3e3692f2:/# du -sh datasets/
1.1G	datasets/서

서버 컴퓨터에서 실제로 Bactopia 분석 작업을 하려면 디렉토리 구성을 어떻게 하는 것이 바람직한지는 약간 머리를 써야 되겠다.

이 과정을 익히기 위하여 다음 웹문서를 주로 참조하였다.

[~/xo.dev] 실행 중인 Docker 컨테이너를 파일로 저장하고 다시 불러오기


삽질 후기

도커 콘테이너 안에서 외부 자료를 가져오려다가 또 SSL 인증서 문제로 막혔다. 어휴... 도커 이미지를 고치느니 적당히 쓰고 말겠다. 물론 도커 잘못은 아니지만.

2021년 11월 3일 수요일

AMD Ryzen 5950X 우분투 데스크탑에 LVM2로 SATA HDD 2개 올리기

1TB SSD로 근근이 버텨왔지만 늘어나는 데이터를 더 이상 감당하기 어려워서 SATA HDD를 추가로 장착하기로 하였다. 재고로 갖고 있던 6TB HDD는 지난달 NAS에 교체용으로 사용하고 말았다(당시 작성한 글 링크). 하드 디스크가 수북하게 쌓여있는 장을 열어 보았다. 바로 그 NAS를 처음 구입하여 사용할 때 장착되어 있던 5개의 HDD를 업그레이드하면서 떼어 놓은 상태로 보관되어 있었다. 용량은 4TB. 이것 두 개면 당장은 부족함이 없으리라.

그런데 이 데스크탑 케이스는 SATA HDD를 도대체 어디에 넣어야 하나? 드라이버를 들고 씨름을 하다가 겨우 공간을 찾아냈다. 심각한 수준의 케이스 & 파워 서플라이가 아니라서 2-3개 정도의 HDD를 추가로 달 수 있었다. 파워 커넥터기 본체와 미묘하게 걸려서 HDD의 고정 위치를바꾸라 시간을 조금 더 소비하였다. 첫 부팅에서 BIOS 셋업으로 진입하여 디바이스가 잘 인식됨을 확인한 뒤 AHCI 모드로 설정하였다. 만약 이것은 RAID 모드로 한다면 LVM의 도움 없이 여러 디스크를 하나로 합쳐서 쓸 수 있을까? 그것도 우분투에서? 그건 나도 모르겠다.

LMV(Logical Volume Manager) 설정을 어떻게 하더라? 블로그의 기록으로는 2012-2013년 무렵에 조립 랙마운트 서버에서 5개의 HDD를 LVM으로 묶어서 사용하면서 겪었던 오류를 복구하던 약간의 경험이 남아 있다. 그 후로 RAID 콘트롤러 카드가 달린 서버를 새로 구입하면서 LVM은 까맣게 잊고 있었다. 이번에 LVM을 쓰려는 것은 단지 두 개의 물리적 HDD를 하나로 합쳐서 큰 용량으로 쓰기 위함이다. 저장 매체의 오류에 대비하려는 것은 아니다.

Disks GUI 유틸리티에서 이전의 파티션 정보를 전부 지우고 각 디스크마다 하나의 파티션을 생성하였다. 이 화면을 보려면 명령행에서는 'gnome-disks'라고 입력하면 된다.

그 다음으로는 명령행에서 작업을 수행하였다. LVM 설정을 위한 GUI 도구가 있을법도 한데 우분투에서 사용 가능한 것을 찾지 못하였다. 놀랍게도(!) lvm2는 아예 설치가 되어 있지 않았다. 구글을 뒤져 가면서 찬찬히 진행해 보았다. lvcreate로 작은 크기의 논리 볼륨(logical volume)을 먼저 만든 뒤 최대 크기로 확장(lvextend 명령 사용)하는 과정을 이해하기가 조금 어려웠다. 처음부터 최대 용량으로 하면 안되나?

$ sudo pvcreate /dev/sda1
$ sudo pvcreate /dev/sdb1
$ sudo vgcreate VolGroup00 /dev/sda1 /dev/sdb1
$ sudo lvcreate -l 20 -n logical_vol0 VolGroup00
$ sudo lvdisplay
$ sudo lvextend -l 100%FREE /dev/VolGroup00/logical_vol0
$ sudo mkfs.ext4 /dev/VolGroup00/logical_vol0
$ sudo fsck -y /dev/VolGroup00/logical_vol0
$ sudo mkdir /work
$ sudo mount /dev/VolGroup00/logical_vol0 /work
$ sudo blkid /dev/VolGroup00/logical_vol0

마지막으로 논리적 볼륨에 대한 UUID를 생성한 뒤 이를 /etc/fstab에 기록하여 재부팅 시에도 알아서 마운트가 되게 하였다. 

물리적 볼륨, 볼륨 그룹, 그리고 논리적 볼륨이라는 계층 구조로 구성되는 LVM의 체계를 이해한다면 GUI 도구가 없어도 설정을 하는데 큰 어려움은 없을 것이다. 다음의 스크린샷은 LVM 설정을 마친 뒤 Disks의 화면을 보이고 있다.

10년이 넘은 컴퓨터를 앞으로 얼마나 더 쓸 수 있을까? 정년 퇴직 전까지 도전을 해 보자.


LVM에서도 RAID4/5/6이 된다고?

Red Hat Enterprise 6.3부터 LVM은 RAID4/5/6을 지원한다는 글을 찾게 되었다(링크). 그렇다면 매우 고무적인 일이 아닐 수 없다. 우분투에서도 되지 않을까?


RAID 5를 쓰려면 최소 3개의 디스크 드라이브가 필요하다. Ryzen 데스크탑에 SATA 디스크 드라이브를 하나 더 쑤셔 넣을 수만 있다면 불가능한 일도 아니다. 


2021년 11월 1일 월요일

뭔가를 만들고 싶다는 생각(착각?)

오디오 DIY와 관련해서 현재로서는 시급하게 해결할 문제를 갖고 있지 않다. 손가락이 너무 심심해서 지난 주말에는 PCL86 싱글 앰프의 잘못된 그라운드 배선을 개선하였다. 이제 뭘 해야 하는가? 이래서 많은 자작인들이 직접 만든 앰프를 하나 둘씩 남에게 선물하거나 처분하기 시작하는 것 같다.

그런데 상용 220V 전원에 직접 연결하여 작동하는 물건을 만들어서 함부로 팔았다가는 문제가 될 수 있다. 유튜브에서 공돌이파파 님의  '진공관 앰프 자작의 불편한 진실'이라는 동영상을 소개해 본다.

모든 일에는 책임이 따르는 법이다. 많은 자작인들이 여러 경로를 통해서 직접 만든 진공관 앰프를 팔고는 있지만, 결함으로 인해 화재가 나거나 감전 사고가 발생한다면 법적인 책임을 벗어나기 어렵다. 키트를 만들어 파는 것도 법적인 요구사항을 슬기롭게(?) 피해 나가는 한 방법일 것이다. 혹은 KC 인증을 받은 상용 DC 어댑터를 통해 저전압 전원을 쓰게 하고, 기기 내부에서는 DC-DC boosting converter를 써서 진공관을 구동하는 고전압을 만들게 하는 것도 아이디어가 될 수 있다. 물론 스위칭 전원은 진공관 앰프(아니, 모든 오디오 앰프?)와 맞지 않는다는 생각을 가진 사람들이 아직 많이 있다.

따라서 나 혼자 즐기기 위한 용도로만 앰프를 만들어야 하는데, 비용이야 어떻게든 조달한다 해도 만든 앰프를 둘 자리가 점점 없어진다. '앰프 총량제'라도 실시하지 않는다면 정말 곤란하다. 아니면 취미를 위한 별도의 공간이 있는 넓은 집으로 이사를 가야 한다.

6LQ8 진공관은 8개가 남았다.

만약 다음 프로젝트를 구상한다면 무엇이 그 대상이 될까? Push-pull? 출력관은 무엇으로? 혹은 반도체 파워 앰프가 이미 있으니 다른 종류의 진공관을 써서 간단히 만드는 재미를 즐길 것인가?