kuroの覚え書き

96の個人的覚え書き

Slurmでノードのstateがdrainedになってしまうとき

サブシステムをクラスタ化しようとしてちょっとハマった。
メインシステムはほぼ同じスペックで各ノード256GBメモリを積んでおり、特に問題なくSlurmがインストールできて

NODELIST       NODES PARTITION       STATE CPUS    S:C:T MEMORY TMP_DISK WEIGHT AVAIL_FE REASON              
node3-a6      1     work*        idle 48     1:24:2 256000        0      1   (null) none                
node4-a6      1     work*        idle 48     1:24:2 256000        0      1   (null) none                
node5-a6      1     work*        idle 48     1:24:2 256000        0      1   (null) none

こんな感じ。
ところが寄せ集めのサブシステムではなんだかうまくいかず、

NODELIST       NODES PARTITION       STATE CPUS    S:C:T MEMORY TMP_DISK WEIGHT AVAIL_FE REASON              
node6-x2110      1     work*     drained 8       1:4:2  32000        0      1   (null) Low RealMemory      
node7-r620        1     work*        idle 32      2:8:2  96000        0      1   (null) none                

このようにLowRealMemoryというエラーがついてstateがdrainedとなってしまう。
問題のノードの実メモリ

$ cat /proc/meminfo 
MemTotal:       32583320 kB
MemFree:        31144288 kB
MemAvailable:   31471072 kB
Buffers:            4664 kB
Cached:           686380 kB
SwapCached:            0 kB
Active:           177208 kB
Inactive:         918404 kB
Active(anon):       2884 kB
Inactive(anon):   425188 kB
Active(file):     174324 kB
Inactive(file):   493216 kB
Unevictable:       25072 kB
Mlocked:           21984 kB
SwapTotal:       8241148 kB
SwapFree:        8241148 kB
Dirty:                 0 kB
Writeback:             0 kB
AnonPages:        411780 kB
Mapped:           235504 kB
Shmem:             19008 kB
KReclaimable:      53572 kB
Slab:             124156 kB
SReclaimable:      53572 kB
SUnreclaim:        70584 kB
KernelStack:        7296 kB
PageTables:        23696 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:    24532808 kB
Committed_AS:    2863272 kB
VmallocTotal:   34359738367 kB
VmallocUsed:       84148 kB
VmallocChunk:          0 kB
Percpu:             5248 kB
HardwareCorrupted:     0 kB
AnonHugePages:    116736 kB
ShmemHugePages:        0 kB
ShmemPmdMapped:        0 kB
FileHugePages:         0 kB
FilePmdMapped:         0 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
Hugetlb:               0 kB
DirectMap4k:      202340 kB
DirectMap2M:     6031360 kB
DirectMap1G:    27262976 kB

ということで32GBなので32000 (MB)として/etc/slurm/slurm.confで

# COMPUTE NODES
NodeName=node6-x2110 CPUs=8 Sockets=1 CoresPerSocket=4 ThreadsPerCore=2 RealMemory=32000 State=UNKNOWN
NodeName=node7-r620 CPUs=32 Sockets=2 CoresPerSocket=8 ThreadsPerCore=2 RealMemory=96000 State=UNKNOWN
PartitionName=work Nodes=ALL OverSubscribe=FORCE Default=YES MaxTime=INFINITE State=UP

というふうに設定したのだが。

slurmd -Cというコマンドでどういうふうに認識されているかを見る方法があるということなので

$ slurmd -C
NodeName=node6-x2110 CPUs=8 Boards=1 SocketsPerBoard=1 CoresPerSocket=4 ThreadsPerCore=2 RealMemory=31819
UpTime=0-00:53:14

おやおや?31819と認識されているよ?

ってことで実際に載せているメモリよりちょっと小さめに設定しておくのが吉らしい。
RealMemory=30000
としてやり、

$ sudo systemctl stop slurmctld
$ sudo systemctl stop slurmd
$ sudo systemctl start slurmd
$ sudo systemctl start slurmctld

さらに

$ sudo scontrol reconfigure
$ sudo scontrol update nodename=node6-x2110 state=resume

としてやってようやく

$ sinfo -N -l

NODELIST       NODES PARTITION       STATE CPUS    S:C:T MEMORY TMP_DISK WEIGHT AVAIL_FE REASON              
node6-x2110      1     work*     idle 8       1:4:2  30000        0      1   (null) none                
node7-r620       1     work*     idle 32      2:8:2  95000        0      1   (null) none 

無事使えるようになった。

ネットワークの冗長化

サーバとして売られているPCにはたいていネットワークコネクタ(NIC)が2つ以上ついている。
場合によっては2つのNICを使ってネットワークをブリッジさせる目的に使われるが、多くは冗長化によるネットワークの安定を図る目的で使われていると思う。
チーミングやボンディングと呼ばれる手法だな。これはあくまで片方のNICの具合が悪くなってももう片方で正常に通信できるようにしておくことが目的なので、ネットワークの帯域が広がったりはしない。そういう設定もあるらしいが。

$ sudo dnf install -y teamd

AlmaLinux8.9ではサーバとしてインストールしたら最初からインストールされている。

NICチーミングを設定するには、/etc/sysconfig/network-scripts/ディレクトリに設定ファイルを作成する。
例えば、ifcfg-team0という名前のファイルを作成する。

$ sudo nano /etc/sysconfig/network-scripts/ifcfg-team0

DEVICE=team0
DEVICETYPE=Team
ONBOOT=yes
TEAM_CONFIG='{"runner": {"name": "activebackup"}}'

そしてイーサネットケーブルを繋いでいたNICに割り当てていたIPをここに設定する。

IPADDR=10.0.1.10
PREFIX=24
GATEWAY=10.0.1.1
DNS1=8.8.8.8

など。

そしてもともとのNICの設定ファイルを開く。

$ sudo nano /etc/sysconfig/network-scripts/ifcfg-<NIC1>

DEVICE=<NIC1>
ONBOOT=yes
TEAM_MASTER=team0
DEVICETYPE=TeamPort

もともと書かれていた設定とかぶるところだけ書き換えで、その他はそのまま残しておく

NICのUUIDをもとにHDDをマウントしたりすることがあるので、team0にUUIDをつけておくとよい。

$ sudo uuidgen

これで生成された文字列を

$ sudo nano /etc/sysconfig/network-scripts/ifcfg-team0

UUID= <文字列>

と足しておく。
これで再起動すればOK。

Slurmの計算ノードのSTATEがdownになったとき

Slurmを運用していて、原因不明で計算ノードが動かなくなった。
sinfoで確認すると

# sinfo
PARTITION AVAIL  TIMELIMIT  NODES  STATE NODELIST
work*        up   infinite      1   idle kk3
work*        up   infinite      2   down kk4,kk5

みたいな感じ。
それぞれのノードでslurmdのステータスを確認しても

# systemctl status slurmd
● slurmd.service - Slurm node daemon
   Loaded: loaded (/usr/lib/systemd/system/slurmd.service; enabled; vendor pres>
   Active: active (running) since Sun 2024-01-07 11:33:23 JST; 1 weeks 1 days a>
 Main PID: 2023 (slurmd)
    Tasks: 1
   Memory: 5.3M
   CGroup: /system.slice/slurmd.service
           └─2023 /usr/sbin/slurmd -D

のようにactiveだ、と言われる。


対処法。
管理ノード(slurmctldの動いているノード)で

$ sudo scontrol update nodename=kk4 state=resume
$ sudo scontrol update nodename=kk5 state=resume

とする。

bowtie2-RSEM

bowtie2によるローカルアライメントを使い、Poly-Tプライマーで作成したcDNAライブラリのRNA-seq解析を行う。

まずはbowtie2をインストールする。

$ git clone https://github.com/BenLangmead/bowtie2.git
$ cd bowtie2/
$ make
$ sudo make install

次にリファレンスのインデックスを作成する。

$ cd PATH/TO/reference
$ mkdir target_ref_bowtie2index
$ bowtie2-build --threads 40 ./genome_assembly.fasta ./target_ref_bowtie2index/genome_assembly

ゲノムのサイズにもよるがマルチスレッドを使ってもかなりの時間を要する。


ローカルアライメントオプションを入れてマッピングしてみる

bowtie2 -p 40 -x [リファレンスゲノムのインデックスへのパス] -U [RNA-seqデータのファイル.fastq.gz] --local -k 1 -S [出力SAMファイル名.sam] 2> mapping_report.txt

標準ではエラー出力にしかレポートが出力されないため、バッチ処理等で大量にまとめて処理するとレポートが取れない。なので2>でファイルにリダイレクトしている。
また-k 1オプションにより、1つのリードが複数箇所にマッピングされるときは最良の1箇所だけを残す選択をしている。(これは必須ではない)

このあと生成したSAMをsamtoolsでBAMに変換、ソート、インデックス作成までは問題なく実施できた。
これをRSEMで発現解析させたいのだが、まだ上手く行っていない。

追記
どうもうまく行かない。
結局bowtie2で前もってゲノムにマッピングしておいて、それをRSEMで作成したtranscriptデータベースを使って発現解析をするという手順に問題がありそうだ。最初からbowtie2のtranscriptインデックスを作成してそちらにマッピング〜発現解析までをRSEMで一括して行うのが良さそう。

まずRSEMでtranscriptデータベースを作成する。

$ rsem-prepare-reference -p 40 --bowtie2 --bowtie2-path /usr/local/bin --gff3 genes.gff3 genome.fa rsem_db/transcript_db

ポイントとしては--bowtie2-pathはあくまでbowtie2のあるディレクトリのパスであり、bowtie2まで入れないこと。このコマンドでRSEM用の発現解析用データベースと一緒にbowtie2のマッピング用インデックスも生成される。

そしてbowtie2でのマッピングもRSEMの中でやってしまう。

$ rem-calculate-expression -p 40 --bowtie2 --bowtie2-path /usr/local/bin   input_reads_1.fastq.gz  --bowtie2-options "--local"  rsem_db/transcript_db output_prefix

と思ったが、RSEMでは--localオプションを扱えないらしい。上のようなオプションの付け方は無効だ。そのままend-to-endで解析すると結果としてマップ率が非常に低くなってしまうため、トリミングの時点で3’末端を削っておく必要がある。(以下の例では50base削った)

fastp --trim_tail1 50 -w 40 -i  input_reads_1.fastq.gz -o  input_reads_1_trim50.fastq.gz -q 20 -l 20 -j input_reads_1_trim_report50.json -h input_reads_1_trim_report50.html

そのうえで、

$ rsem-calculate-expression -p 40 --bowtie2 --bowtie2-path /usr/local/bin input_reads_1_trim50.fastq.gz  rsem_db/transcript_db output_prefix 2> mapping_report.txt

レポートのリダイレクトも有効だ。

マッピングレートは--localオプションを付けると90%以上であったが、トリミングしてのend-to-endアライメントではせいぜい60%くらいにしかならなかった。

さて、これでとりあえずリードカウントができることがわかったが、少しまだ問題が残る。
上記パイプラインをコマンドラインで順番に処理する上では問題がないのだが、クラスタサーバでslurmを使ったジョブスケジューリングで自動運転した場合、RSEM内部で呼び出しているsamtoolsがうまく見つけてもらえない。bowtie2は明示的にパスを与えているのだが、samtoolsにはそのオプションがなく、処理が止まってしまうようだ。

なので、やはりbowtie2によるマッピングはRSEM内部で処理せず、個別に実施した上で、RSEMではリードカウントのみ実施してもらうのが良さそうだ。

そこで、

$ bowtie2 -p 40 -x rsem_db/transcript_db -U input_reads_1_trim50.fastq.gz -S output_prefix.sam 2> output_prefix_mapping_report.txt
$ samtools view -@ 40 -bS output_prefix.sam > output_prefix.bam
$ samtools sort-@ 40 output_prefix.bam -o output_prefix_sort.bam
$ samtools index output_prefix_sort.bam
$ rsem-calculate-expression -p 40 --alignments output_prefix_sort.bam rsem_db/transcript_db output_prefix

というパイプラインにしてみたのだが、
どういうわけか

rsem-parse-alignments rsem_db/transcript_db output_prefix.temp/output_prefix output_prefix.stat/output_prefix output_prefix.transcript_sort.bam 1 -tag XM
Read LH00220:78:22H5YFLT3:7:1284:39149:5518: RSEM currently does not support gapped alignments, sorry!

"rsem-parse-alignments rsem_db/transcript_db output_prefix.temp/output_prefix output_prefix.stat/output_prefix output_prefix.transcript_sort.bam 1 -tag XM" failed! Plase check if you provide correct parameters/options for the pipeline!

というエラーが出る。どうもbowtie2は厳密にギャップアライメントを排除することが難しく、素のbowtie2のデフォルトオプションとRSEM内部でやっているオプションが違うらしい。
RSEMのヘルプの‐‐bowtie2オプションの説明にヒントが有った。

--bowtie2
        Use Bowtie 2 instead of Bowtie to align reads. Since currently RSEM
        does not handle indel, local and discordant alignments, the Bowtie2
        parameters are set in a way to avoid those alignments. In
        particular, we use options '--sensitive --dpad 0 --gbar 99999999
        --mp 1,1 --np 1 --score-min L,0,-0.1' by default. The last parameter
        of '--score-min', '-0.1', is the negative of maximum mismatch rate.
        This rate can be set by option '--bowtie2-mismatch-rate'. If reads
        are paired-end, we additionally use options '--no-mixed' and
        '--no-discordant'. (Default: off)

つまり

--sensitive --dpad 0 --gbar 99999999 --mp 1,1 --np 1 --score-min L,0,-0.1

というオプションがデフォルトで入っているらしい。
なので上のパイプラインの1行目を

$ bowtie2 --sensitive --dpad 0 --gbar 99999999 --mp 1,1 --np 1 --score-min L,0,-0.1 -p 40 -x rsem_db/transcript_db -U input_reads_1_trim50.fastq.gz -S output_prefix.sam 2> output_prefix_mapping_report.txt

としてやることでRSEM内部での動作と同等にしてやることが可能となった。

今後ローカルアライメントを受け付けるリードカウントについては検討の余地があると思う。そもそもbowtie2を使いたかったのもローカルアライメントが使えるからであり、それが使えないならSTARでマッピングしても同じようなものだから。bowtie2を内部で実施しないなら、そもそもRSEMを使う必然性も低く、これにかわるリードカウント方法を考えてもいいように思う。RSEMのカウントが比較的正確だ、というのもあるけど。

Flask2.0.0(3.0.0)のBlueprintの使い方

Flask1系から2系にアップデートしたとき、blueprintの使い方がちょっと変わっていてトラブったので覚書。


manage.py   ## 2系以降は不要
sqlite.db
app----------+
             |-__init__.py
             |-config.py
             |-models.py
             |-views---------+
             |               |-home.py
             |               |-tools.py
             |
             |-templates-----+
             |               |-index.html
             |               |.........
             |-static           

こういう構成だとして
1系のときは
__init__.py

from flask import Flask

app = Flask(__name__)
app.config.from_object('app.config')

中略

#Blueprint
from app.views import home, tools
                    
app.register_blueprint(home.app)
app.register_blueprint(tools.app)

app/home.py

from functools import wraps
from flask import request, redirect, url_for, render_template, flash, g, Blueprint
from flask import session as fl_session
from ..models import User
from app import app, Session
import sys
sys.setrecursionlimit(100000)

app = Blueprint('home', __name__)

################################################################################
def login_required(f):
	@wraps(f)
	def decorated_view(*args, **kwargs):
		if g.user is None:
			return redirect(url_for('login', next=request.path))
		return f(*args, **kwargs)
	return decorated_view

@app.before_request
def load_user():
	user_id = fl_session.get('user_id')
	if user_id is None:
		g.user = None
	else:
		g.user = Session().query(User).get(fl_session['user_id'])

@app.errorhandler(404)
def page_not_found(e):
	return render_template('404.html'), 404

@app.errorhandler(500)
def internal_server_error(e):
	return render_template('500.html'), 500

##############################################################################
#アプリルート
@app.route('/')
def index():
	return render_template('index.html')
##############################################################################
#ログイン・アウト管理
@app.route('/login', methods=['GET', 'POST'])
def login():
	if request.method == 'POST':
		user, authenticated = User.authenticate(Session().query, request.form['email'], request.form['password'])
		if authenticated:
			fl_session['user_id'] = user.id
			flash('You were logged in')
			return redirect(url_for('index'))
		else:
			flash('Invalid email or password')
	return render_template('login.html')

@app.route('/logout')
def logout():
	fl_session.pop('user_id', None)
	flash('You were logged out')
	return redirect(url_for('index'))

このような感じであったが、
2系以降では

__init__.py

from flask import Flask

app = Flask(__name__)
app.config.from_object('app.config')

中略

#Blueprint
from app.views.home import home_bp
from app.views.tools import tools_bp

app.register_blueprint(home_bp)
app.register_blueprint(tools_bp)

home.py

from functools import wraps
from flask import request, redirect, url_for, render_template, flash, g, Blueprint
from flask import session as fl_session
from ..models import User
from app import app, Session
import sys
sys.setrecursionlimit(100000)

home_bp = Blueprint('home', __name__)

################################################################################
def login_required(f):
	@wraps(f)
	def decorated_view(*args, **kwargs):
		if g.user is None:
			return redirect(url_for('home.login', next=request.path))
		return f(*args, **kwargs)
	return decorated_view

@home_bp.before_request
def load_user():
	user_id = fl_session.get('user_id')
	if user_id is None:
		g.user = None
	else:
		g.user = Session().query(User).get(fl_session['user_id'])

@home_bp.errorhandler(404)
def page_not_found(e):
	return render_template('404.html'), 404

@home_bp.errorhandler(500)
def internal_server_error(e):
	return render_template('500.html'), 500

##############################################################################
#アプリルート
@home_bp.route('/')
def index():
	return render_template('index.html')
##############################################################################
#ログイン・アウト管理
@home_bp.route('/login', methods=['GET', 'POST'])
def login():
	if request.method == 'POST':
		user, authenticated = User.authenticate(Session().query, request.form['email'], request.form['password'])
		if authenticated:
			fl_session['user_id'] = user.id
			flash('You were logged in')
			return redirect(url_for('home.index'))
		else:
			flash('Invalid email or password')
	return render_template('login.html')

@home_bp.route('/logout')
def logout():
	fl_session.pop('user_id', None)
	flash('You were logged out')
	return redirect(url_for('home.index'))

このようになる。
一度書き換えてしまえばこの方が分類がはっきりしてわかりやすい書き方になっていると思う。1系の書き方ではappがあっちにもこっちにも出てきて繋がりが分かりにくかったので。

1系の書き方のままでも2系の大方の動作には問題がないのだが、Loginまわりが軒並み動かなくなるので、書き換えたほうがいいと思う。おそらくurl_forの書き方が変わっているのが原因だと思う。

なお2系ではFlask_scriptによる python manage.py runserverによる起動はできないので、manage.pyは使わない。flask runコマンドで起動する。

Flaskアプリでbotのアクセスを受けたときに拒否する

Flaskのアクセスログを見ていると半分以上がbotのアクセスであった。さらに、過去に生成して、時間経過のために消去した一時ファイルを参照していたりするので、ことごとくエラーになり、無駄な処理をさせられまくっていたので特にしつこくアクセスしてくるIPは一括でブロックすることにした。

from flask import Flask, request, abort

app = Flask(__name__)

# ブロックしたいIPアドレスのプレフィックスを指定
BLOCKED_IP_PREFIXES = ['85.208.96.', '185.191.171.']

@app.before_request
def block_ip():
    # アクセス元IPアドレスを取得
    remote_ip = request.remote_addr

    # ブロックされたIPアドレスの場合は403 Forbiddenを返す
    for prefix in BLOCKED_IP_PREFIXES:
        if remote_ip.startswith(prefix):
            abort(403)

# 通常のルート
@app.route('/')
def index():
    return 'Welcome to the site!'

if __name__ == '__main__':
    app.run(debug=True)

こんな感じ。
とりあえず403をすぐ打ち返し、スクリプトに入らないようにした。これで多少はトラフィックが減ってくれるといいのだが。でもこれはきっといたちごっこになるんだろうな。

RSEMのインストール

今更ですが。

これまでメインにはStringTieを発現解析に使っていたが、RSEMも使ってみようと。

現状miniconda3の上に環境構築をしているので、

$ conda install rsem

としてみる。その結果、

$ which rsem-calculate-expression
~/miniconda3/envs/ngs/bin/rsem-calculate-expression

$ rsem-calculate-expression --version
Current version: RSEM v1.2.28

この様にインストールされているのがわかった。
現行バージョンが1.3.3ということなんで、1.2.28というとかなり古い。なのでソースからとってくる
deweylab.github.io

適当な場所に解凍し、そのディレクトリに入って

$ make

とするだけでいいらしい。
なのでやってみたが、早速エラーが出た。なんかperlのライブラリがいるらしい。perl・・・嫌な予感。

Can't locate Env.pm in @INC (you may need to install the Env module) (@INC contains: /home/rnaseq/RSEM-1.3.3 /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5) at /home/rnaseq/RSEM-1.3.3/rsem-calculate-expression line 10.
BEGIN failed--compilation aborted at /home/rnaseq/RSEM-1.3.3/rsem-calculate-expression line 10.

PerlのEnvモジュールをインストールするため

$ cpan Env

とするもcpanが入ってない。

$ sudo dnf install perl-CPAN
$ sudo cpan

これでやっとEnvがインストールできる。
cpanの中で

> install Env

Reading '/root/.local/share/.cpan/Metadata'
  Database was generated on Tue, 19 Dec 2023 05:54:00 GMT
Running install for module 'Env'
Checksum for /root/.local/share/.cpan/sources/authors/id/F/FL/FLORA/Env-1.04.tar.gz ok
Scanning cache /root/.local/share/.cpan/build for sizes
............................................................................DONE
'YAML' not installed, will not store persistent state
Configuring F/FL/FLORA/Env-1.04.tar.gz with Makefile.PL
Checking if your kit is complete...
Looks good
Generating a Unix-style Makefile
Writing Makefile for Env
Writing MYMETA.yml and MYMETA.json
  FLORA/Env-1.04.tar.gz
  /usr/bin/perl Makefile.PL -- OK
Running make for F/FL/FLORA/Env-1.04.tar.gz
cp lib/Env.pm blib/lib/Env.pm
Manifying 1 pod document
  FLORA/Env-1.04.tar.gz
  /bin/make -- OK
Running make test
PERL_DL_NONLAZY=1 "/usr/bin/perl" "-MExtUtils::Command::MM" "-MTest::Harness" "-e" "undef *Test::Harness::Switches; test_harness(0, 'blib/lib', 'blib/arch')" t/*.t
t/array.t ............... Can't locate Test/More.pm in @INC (you may need to install the Test::More module) (@INC contains: /root/.local/share/.cpan/build/Env-1.04-1/blib/lib /root/.local/share/.cpan/build/Env-1.04-1/blib/arch /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 .) at t/array.t line 6.
BEGIN failed--compilation aborted at t/array.t line 6.
t/array.t ............... Dubious, test returned 2 (wstat 512, 0x200)
No subtests run 
t/env.t ................. Can't locate Test/More.pm in @INC (you may need to install the Test::More module) (@INC contains: /root/.local/share/.cpan/build/Env-1.04-1/blib/lib /root/.local/share/.cpan/build/Env-1.04-1/blib/arch /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 .) at t/env.t line 9.
BEGIN failed--compilation aborted at t/env.t line 9.
t/env.t ................. Dubious, test returned 2 (wstat 512, 0x200)
No subtests run 
t/release-pod-syntax.t .. Can't locate Test/More.pm in @INC (you may need to install the Test::More module) (@INC contains: /root/.local/share/.cpan/build/Env-1.04-1/blib/lib /root/.local/share/.cpan/build/Env-1.04-1/blib/arch /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 .) at t/release-pod-syntax.t line 5.
BEGIN failed--compilation aborted at t/release-pod-syntax.t line 8.
t/release-pod-syntax.t .. Dubious, test returned 2 (wstat 512, 0x200)
No subtests run 

Test Summary Report
-------------------
t/array.t             (Wstat: 512 Tests: 0 Failed: 0)
  Non-zero exit status: 2
  Parse errors: No plan found in TAP output
t/env.t               (Wstat: 512 Tests: 0 Failed: 0)
  Non-zero exit status: 2
  Parse errors: No plan found in TAP output
t/release-pod-syntax.t (Wstat: 512 Tests: 0 Failed: 0)
  Non-zero exit status: 2
  Parse errors: No plan found in TAP output
Files=3, Tests=0,  0 wallclock secs ( 0.01 usr +  0.00 sys =  0.01 CPU)
Result: FAIL
Failed 3/3 test programs. 0/0 subtests failed.
make: *** [Makefile:841: test_dynamic] ??? 2
  FLORA/Env-1.04.tar.gz
  /bin/make test -- NOT OK
//hint// to see the cpan-testers results for installing this module, try:
  reports FLORA/Env-1.04.tar.gz
Failed during this command:
 FLORA/Env-1.04.tar.gz                        : make_test NO

またまた大量のエラー。今度はTest::Moreモジュールが見つからないという問題らしい。
cpanの中で

> install Test::More

これでやっとEnvがインストールできる(その2)。

> install Env

でRSEMのディレクトリに戻って、

$ make clean
$ make
$ export PATH=/path/to/RSEM:$PATH

なんとかインストールできた。exportは.bash_profileにも書いておこう。

$ which rsem-calculate-expression
~/RSEM-1.3.3/rsem-calculate-expression
$ rsem-calculate-expression --version
Current version: RSEM v1.3.1

これで使えるようになった(はず)。

RNA-seqデータをde novo アッセンブリーしてUTR配列を含むトランスクリプトアノテーションを作成する。

前提:ゲノム配列の詳細な解析は実施されている。遺伝子予測はCDS領域に限定して実施されている。
やりたいこと:5’3’UTRがどこまでか推測し、cDNA領域のアノテーションを作る。

1. Trinityでde novoアセンブリを実行:
Trinityを使用してRNA-seqデータからde novoアセンブリを行う。

Trinity --CPU 8 --max_memory 24G --seqType fq --left reads_1.fq --right reads_2.fq --output trinity_output

single end readのときは

Trinity --CPU 8 --max_memory 24G  --seqType fq --single reads_1.fq,reads_2.fq --output trinity_output

複数のfastqファイルを使うときはコンマで区切る必要がある。reads_*.fqのようにすると間がスペースとなるのでエラーが出るようだ。

2. TransDecoderを使用してCDS(Coding Sequence)を抽出:
Trinityの出力からCDSを抽出するために、TransDecoderを使用する。

TransDecoder.LongOrfs -t trinity_output/Trinity.fasta
TransDecoder.Predict -t trinity_output/Trinity.fasta

これにより、CDSが Trinity.fasta.transdecoder.pep と Trinity.fasta.transdecoder.cds に保存される。

3. BLASTを使用して Trinity.fasta.transdecoder.cdsアセンブリを参照ゲノムにマッピング:
Trinityのアセンブリを参照ゲノムにマッピングし、遺伝子の対応付けを行う。

blastn -num_threads 8 -query  Trinity.fasta.transdecoder.cds -db reference_genome -outfmt 6 -out trinity_vs_reference.blast

4. Blast結果のBED形式への変換:
blast2bedツールを使用してBlast結果をBED形式に変換し、BEDToolsを利用してUTR領域を抽出する。

blast2bed < trinity_vs_reference.blast > trinity_vs_reference.bed
bedtools intersect -a trinity_vs_reference.bed -b reference_annotation.gff -wa -wb > trinity_vs_reference_annotated.bed


5. シェルスクリプトを使用してGFFファイルを生成:
シェルスクリプトを使用して、BEDファイルからGFFファイルを生成する。
以下は、この目的のための簡単なスクリプトの例。

awk 'BEGIN {OFS="\t"} {print $4, "custom_UTR_annotation", "UTR", $2+1, $3, ".", $6, ".", "gene_id " $1 "; transcript_id " $1}' trinity_vs_reference_annotated.bed > custom_UTR_annotation.gff

これでどうかな。

さらにCDSとUTRを一つにまとめたcDNAアノテーションにするには。
6. CDS領域のアノテーションGFF3とUTR領域のアノテーションGFF3の結合:

cat reference_annotation.gff custom_UTR_annotation.gff > merged_annotation.gff3

7. GFF3ファイルとして整形

awk -F'\t' 'BEGIN{OFS=FS} {gsub(/Parent=[^;]+;/,"",$9); if($3=="CDS"){print $1,$2,"exon",$4,$5,$6,$7,$8,$9} else print}' merged_annotation.gff3 > cdna_annotation.gff3

まだ試行錯誤中。
このままではうまくいかない。

SDI-12コマンド

SDI-12インターフェースで接続した機器とのコミュニケーションについて

SDI-12の規格では
Bits per second: 1200
Data bits: 7
Parity: 1
Stop bits: 1
Flow Control: 反転(active LOW)
で通信することになっている。

Arduino,ESP32,M5stack等で接続するなら

Serial1.begin(1200, SERIAL_7E1, RX_pin, TX_pin);

のように起動する。

基本コマンドをホストから送って、センサーから情報を返して貰うというスタイルで通信する。

コマンドをインプットし最後に!をタイプするとコマンドがそこで終わることを示し、コマンドが送信される。
センサーには固有のアドレス(0-9,a-z,A-Zのいずれか)が設定されている(変更も可能)以下コマンド中に'a'となっていたら実際はそこにアドレスの英数字を入れてコマンド入力することを表す。
またアドレスのワイルドカードとして'?'を使う事ができる。

a!
と送るとアドレス'a'のセンサーが応答しているか確認される。

?!
と送ると接続されているセンサーのアドレスがどのように設定されているかが返される。

aI! (エルではなく大文字アイ)
SDI-12互換性レベル、モデル番号、およびファームウェアバージョン番号などを返す。
例: 1I! 113METER TER12 117631800001
この場合センサーアドレス:1, ターゲットセンサーがSDI-12 specification v1.3をサポート, ベンダー:METER, センサーモデル:TER12, センサーバージョン:107, センサーシリアル番号:631800001であることを示す。
基本的に項目間にスペースなく詰めて表示されるらしい。METERの後ろにスペースが3つあるのはMETERがそのように入力したからということ。

aAB!
アドレスaのセンサーをアドレスBに変更するコマンド

aM!
測定
応答例: 00013
センサーアドレス:0, 応答にかかった時間:001, センサーが返す計測値の数:3

aD0!
データ送信を要求する
応答例: 0+2574.15+13.3+51
この場合、アドレス0から3つのデータが返ってきている


続く

ESP32-WROOM32Eで複数のシリアル通信を行う

他の機器との通信をシリアルポートで行いつつ、他の機器とも通信をしたいということで、通常使用しているシリアルポート以外を使えるようにしたい。
ESP32にはハードウェアシリアルポートが3組用意されており、何も宣言しなくても使える通常使用しているSerialはUART0ということらしい。
他にUART1とUART2があるわけで、どのピンがそうかというとUART1はRXD1:IO9, TXD:IO10でUART2がRXD:IO16, TXD:IO17ということらしい。
IO16,IO17は通常アナログポートじゃないピンなのでいいとしてIO9,IO10はそもそも秋月の基板ではピンが引き出されていない。というのもこれらのピン(IO6~11)は内部的にすでに使われちゃっているからという説明である。
ただし、UART1を別のピンにアサインするという技が使えるらしい。
現在テスト基板ではIO34,IO35,IO32にアナログセンサー接続用のコネクタを付けているので、このポートをシリアル通信に流用しようと試してみたところ、IO34とIO35は

collect2: error: ld returned 1 exit status

というエラーが出て使えないことがわかった。当然ながらIO6~11も使えない。16,17をUART1として使うこともできるがあまり意味はないかな。
のこるIO32は無事使えることがわかった。これだとTxかRxのどちらかしか使えないのだが、やむをえまい。
後はWIFIと共存できないADC2チャンネルのIO25〜27のあたりもシリアル通信ならできるんじゃないかな。

HardwareSerial mySerial1(1);
const int Rx1Pin = 25;
const int Tx1Pin =32;

void setup() {
  Serial.begin(115200); //通常のシリアルポートUART0
  mySerial1.begin(115200, SERIAL_8N1,Rx1Pin,Tx1Pin); //UART1をRXD:25,TXD32に割り付ける
}

void loop() {
  mySerial1.print("Hello worls!");