物理のベンチ by mitta

学んだことを発信します。備忘録も書きます。間違いがあればコメントください。

【備忘録】Pythonのserialモジュールは似たようなものが2つある

はじめに

このくそ忙しい時期にRS-232Cのシリアル通信で実験機器のパラメータを早急に変える必要があった。
滅多に行わない処理で、常設のコードなんてものはないので、Pythonでパラメータを先に変えてしまおうと判断した。

問題

シリアル通信のモジュールをpip installし、いざ

import serial
conn = serial.Serial("COM3", baudrate=9600)

>>> AttributeError: module 'serial' has no attribute 'Serial'

serialにSerialがないと怒られる。

原因

Pythonのシリアル通信モジュールには2種類ある
・pySerial
・serial
このうち、pyserialには「Serial」があり、serialにはないらしい。
両方ともimport serialでモジュールをインポートするので、「Serial」が呼ばれない。

対策

pip uninstall serial
pip uninstall pyserial
pip install pyserial

一度全部消してからpyserialのほうをインストールすればよい

所感

少しだけ調べた限り、pyserialを使った記事のほうが多いのでpyserialのみを使用したほうが無難。
筆者のようなネット記事のサンプルコードのimport文だけを見て「pip install serial」をしてしまうPythonをちょっとかじった人がハマりそう。

【備忘録】Classのカッコの有無【Python】

概要

PythonのClassを使用するとき、カッコを付けないと処理が変わる。
たまに大きめの処理をするコードを1から書くことがあり、仕様を忘れて調べる羽目になっているので備忘録を残す。
ついでに、listにclassを入れた後の変更も反映されているか確認する。

カッコ有①

class TmpCls:
    a = 1

li = []
for i in range(4):
    tmp = TmpCls()   #←ここ
    print("値変更前",i,tmp.a)
    li.append(tmp)
    tmp.a = i
    print("値変更後",i,tmp.a)

for ttmp in li:
    print("for外 : a",ttmp.a)

##出力
値変更前 0 1
値変更後 0 0
値変更前 1 1
値変更後 1 1
値変更前 2 1
値変更後 2 2
値変更前 3 1
値変更後 3 3
for外 : a 0
for外 : a 1
for外 : a 2
for外 : a 3

カッコ無①

class TmpCls:
    a = 1

li = []
for i in range(4):
    tmp = TmpCls #←ここ
    print("値変更前",i,tmp.a)
    li.append(tmp)
    tmp.a = i
    print("値変更後",i,tmp.a)

for ttmp in li:
    print("for外 : a",ttmp.a)

##出力
値変更前 0 1
値変更後 0 0
値変更前 1 0
値変更後 1 1
値変更前 2 1
値変更後 2 2
値変更前 3 2
値変更後 3 3
for外 : a 3
for外 : a 3
for外 : a 3
for外 : a 3

カッコを付けない場合は文字通り「tmp=TmpCls」ってコト?

確認

class TmpCls:
    a = 1
    def __init__(self):
        self.b = 7

tmp = TmpCls
print("a =",tmp.a)
print("b =",tmp.b)
##出力
a = 1
>>例外が発生しました: AttributeError
type object 'TmpCls' has no attribute 'b' in line 8
    print("b =",tmp.b)

インスタンスも当然読めないっぽい

VSCodeで.historyを非表示にする方法

はじめに

コード内語句検索をした時、.history内のコードが引っかかるのがあまりにうざすぎたので備忘録を残す。
VSCodeから.historyを非表示にすればファイル自体を消すことなく検索結果からは外れてくれる。
消しても正直問題ない気もする。

方法

  1. [ctrl]+[,]で設定を開く
  2. 「Files:exclude」と入力
  3. 「**/.history」を追加

VSCodeとgithubのSSH接続をした時の備忘録

はじめに

VSCode管理しているワークスペースをgitで同様に管理できるのはよく知られている。
筆者は普段仕事(Private,HTML)用しか使っていなかったため、それ以外(Public,SSH)で管理するためにややてこずった。
今後もあり得るので備忘録を残しておく。備忘録なので説明不足は悪しからず。

環境

※ユーザ名はいずれもjanedoe

計算用(ホスト)

OS -> CentOS7
git -> 1.8.3.1

入力用(ゲスト)

OS->Win10
VSCode

方法

状況

VSCodeのリモートエクスプローラ機能でホストのソースコードを管理したい

ホストPCでgitのSSH設定

① 公開鍵と秘密鍵を作る
② /home/janedoe/.ssh/configに以下の設定を書き加える

Host github
    HostName github.com
    User git
    IdentityFile /home/ janedoe /.ssh/id_ssh_key

この時ホスト名を「github」にするところがポイント。なぜか「git@github.com」だと後々のSSHが失敗する。
③ 公開鍵(.pubの方)の中身をhttps://github.com/settings/keysから「newkey」として貼り付け
SSH接続が通るか確認

$ ssh -T github
Hi junedoe! You've successfully authenticated, but GitHub does not provide shell access.

上の返答があればOK

VSCodeでgit設定

① リモートエクスプローラからホストのプロジェクトファイルを開く
この時、ホスト・ゲスト間でも公開鍵認証を通しているととても楽
② [Ctrl]+[Shift]+[G]でソース管理タブを開いてgitを接続する
③ プロジェクトファイル/.git/configを開いてurlの部分を変更する

[remote "origin"]
	url = github:janedoe/プロジェクト名.git
	fetch = +refs/heads/*:refs/remotes/origin/*

※ここで.ssh/confのHost名の「github」が参照されるので注意
※よく$ git remoteで変更する記事では「git@github.com」で変更しているのでつられないこと

まとめ

・gitのSSH接続はHost名で通らない可能性がある
・.ssh/configのHost名と.git/configのurlのHost部分は合わせておく

VSCodeでpythonがF5キーで実行できないバグ(?)

症状

・ちょっと前から(2022年8月入ってから?) VSCodeで[F5]キーを押しても実行されない状態になった。
・[デバッグの開始][デバッグなしで実行]ともに実行できない。
・一瞬実行時のツールバーみたいなもの(ブレークポイントデバッグするときに使うやつ)が出現するが、何も起こらない。
・コンソールの[出力]タブの[Python]には実行履歴だけが残る。
・エラーは出ない
・右上にある[新しいPythonファイル]と書かれた▷を押すと一応動く

対処

VSCodePython拡張機能が悪さをしているらしい、v2022.8.1までのバージョンに戻すとうまくいく。
今後の改善に期待。

2022/08/18追伸

VSCodeの自動更新を切る方法

自動更新を切っておかないとふとした拍子に最新版になってしまう。

  1. [Ctrl]+[,]で設定を開く
  2. 検索boxにupdate.modeと入力する
  3. [Default]を[None]にして無効化

【2022年2月】CentOS8でyum(dnf)が使えない時の対処法

はじめに

2021年12月31日にCentOS8がEOLになった。続いて2022年2月に公式のURLが切れたことでyum(dnf)のコマンドが使えなくなった。
しばらくはCentOS7にもStreamにも移行する気力はないが、開発はしたいので備忘録を記しておく。
多分2024年6月30日にCentOS7がEOLになった時も同じことになりそうな気がする。
以降yumもdnfも同じことをすればいい(はず)なので特に記さない限り共通の方法を記しておく。

エラー

これが出た人が対象。

[daq@localhost ~]$ su
[root@localhost ~]# yum list
Error: Failed to download metadata for repo 'AppStream': Cannot prepare internal
 mirrorlist: No URLs in mirrorlist

方針

/etc/yum.repos.d/のディレクトリの中にyumのURLを指定できる.repoというファイルがいくつかある。
今回のyumのエラーはURL切れが原因なのでこのURLを別の場所に書き換えてしまえばよい。

あまりうまくいかなかった方法

①「mirrorlist=~」で始まる行をコメントアウト
②「baseurl=~」で始まる行を有効化して参照先のURLを変更
一々エディタで変更するのも面倒なので一括で行う。以下のコマンドを使えばよい。

[daq@localhost ~]$ su
[root@localhost ~]# sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/C
[root@localhost ~]# sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-Linux-*;

↑コマンドが勝手に改行されるが、3つ目のコマンドは1行で張り付けてほしい。

結果

URLのエラーは出ないがvaultのURLが死ぬほど遅くてタイムアウトになる。

うまくいった方法

基本は先ほどと同じだが参照先のURLを公式のvaultから理研にしてしまう。
ただ、理研の場合CentOS8のバージョンによって参照先が違うので以下のコマンドで現在のバージョンを確認する。

[daq@localhost ~]$ su
[root@localhost ~]# cat /etc/redhat-release
CentOS Linux release 8.4.2105

自分のバージョンがあるかどうかの確認はこちら
ftp.riken.jp
あったら先ほどと同じように変更する。

「あまりうまくいかなかった方法」を試していない人向け
[daq@localhost ~]$ su
[root@localhost ~]# sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-Linux-*;
[root@localhost ~]# sed -i 's|#baseurl=http://mirror.centos.org/$contentdir/$releasever/|baseurl=https://ftp.riken.jp/Linux/centos-vault/8.4.2105/|g' /etc/yum.repos.d/CentOS-Linux-*;

↑コマンドが勝手に改行されるが、すべてのコマンドはそれぞれ1行で張り付けてほしい。

「あまりうまくいかなかった方法」を試してしまった人向け
[daq@localhost ~]$ su
[root@localhost ~]# sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-Linux-*;
[root@localhost ~]# sed -i 's|baseurl=http://vault.centos.org/$contentdir/$releasever/|baseurl=https://ftp.riken.jp/Linux/centos-vault/8.4.2105/|g' /etc/yum.repos.d/CentOS-Linux-*;

↑コマンドが勝手に改行されるが、すべてのコマンドはそれぞれ1行で張り付けてほしい。

確認

[daq@localhost ~]$ su
[root@localhost ~]# yum list

これで理研CentOSを提供してくれる限り割と快適にyumを使える。

まとめ

理研様様
上の理研のindexのリンクが切れた場合、原理上はURL先を変えればよい。
が、その時にはCentOS自体が信用できなくなっているのでそもそもOSを変えろ。

【超初心者向け】C++とPyROOTの2方法でテキストデータをrootファイルに変換する

今回やること

C++Pythonでテキストデータをrootファイルに変換する。

方法

今回はC++を使ったオーソドックスな方法とPyROOTを使った方法を紹介する。
ちなみに完成系はこのような形になる。
f:id:salamence_andrias:20210718022321p:plain

C++を使った方法

筆者はCentOS8を使用。真似したい場合は以下の記事を参照。
phbe.hateblo.jp
CERN ROOTを入れいない人は以下の記事を参照。
phbe.hateblo.jp

作成の流れ
  1. データの準備
  2. makefileの作成
  3. コードの作成
  4. コンパイル、実行
データの準備

今回は以下の記事で作ったテキストデータをそのまま使用する。
phbe.hateblo.jp

makefileの作成

C++で作ったコードは一度コンパイルしてから実行する。
コンパイルを楽にするおまじない的なものがmakefileだ。
ファイルの名前もそのままmakefileにして作成する。
よくわからない人は丸写しを推奨する。

TARGET = mkNT

SRCS = $(TARGET).cc OBJS = $(TARGET).o

ROOTFLAGS = $(shell root-config --cflags) ROOTLIBS = $(shell
root-config --libs) CXXFLAGS = -Wall -O2 $(ROOTFLAGS) CXXLIBS =
$(ROOTLIBS) CC = g++

all: $(TARGET)

$(TARGET): $(OBJS) $(CC) $(CXXLIBS) $(OBJS) -o $@
	$(TARGET).o:$(TARGET).cc $(CC) $(CXXFLAGS) -c $< clean: rm -f
	$(TARGET) $(OBJS)

1行目の「mkNT」というのが今回作りたいコードの名前である。
「mkNT.cc」という名前のC++コードをコンパイルできるようになった。
もちろん別の名前のコードをコンパイルしたい場合は「mkNT」の部分をその名前に変えて実行する。
複数のコードを同時にコンパイルする方法もあるが、今回は触れない。


コードの作成

コード作成前に今回のデータを整理する。
今回のデータは以下のように書かれている

イベント番号 ADC値 TDC値1~3 ランダム数1~6
整数 整数 整数 小数

このうちTDC値を1次元配列に、ランダム数を2次元配列にしてみる

#include "TApplication.h"
#include <math.h>
#include <TROOT.h>
#include <TNtuple.h>
#include <TStyle.h>
#include <TFile.h>
#include <fstream>
#include <iostream>

using namespace std;
const char kNameInFile[20] = "output.dat";
const char kNameOutFile[20] = "output.root";


int main(){
  cout << "Input File  = " << kNameInFile << endl;
  cout << "Output File = " << kNameOutFile << endl;

  // define value
  Int_t ieve;
  Int_t adc;
  Int_t tdc[3];
  Double_t rand[2][3];

  //open root
  gROOT->SetStyle("Plain");
  gStyle->SetOptStat("neiuoMR");
  gStyle->SetPalette(1);
  TTree *tree = new TTree("raw", "raw tree");
  tree->Branch("ieve", &ieve, "ieve/I");
  tree->Branch("adc", &adc, "adc/I");
  tree->Branch("tdc",tdc, "tdc[3]/I");
  tree->Branch("rand",rand, "rand[2][3]/D");

  // open dat file
  ifstream indata(kNameInFile);
  
  // file exist check
  if (indata.fail()) {
    cout << "### program err ###" << endl;
    std::cerr << "!!!Failed to open file!!! \n!!!Not found this file!!!"
	      << std::endl;
    cout << "### program close ###" << endl;
        return -1;
    }
  
  // get data & write
  while (indata >>
	 ieve >> adc >> tdc[0] >> tdc[1] >> tdc[2] >>
	 rand[0][0]>> rand[0][1]>> rand[0][2]>>
	 rand[1][0]>> rand[1][1]>> rand[1][2] ) {
    tree->Fill();
     if (ieve%1000 == 0){
      cout << "event_loop = "<< ieve << endl;}
  }
  cout << "event num = " << ieve+1 << endl;
  indata.close();

  // write rootfile
  TFile *fout = new TFile(kNameOutFile, "recreate");
  tree->Write();   
  fout->Close();

  // end
  cout << "F!!!!!!!!!!!!!!!!!!!!!!" << endl;
  return 0;
}


コンパイル、実行

C++のコードが書けたら、terminalからコンパイル

[***@localhost **] $ make

エラーとかが出なければコンパイル完了なのでそのまま実行。

[***@localhost **] $ ./mkNT
Input File  = output.dat
Output File = output.root
event_loop = 0
event_loop = 1000
event_loop = 2000
event_loop = 3000
event_loop = 4000
event_loop = 5000
event_loop = 6000
event_loop = 7000
event_loop = 8000
event_loop = 9000
event num = 10000
F!!!!!!!!!!!!!!!!!!!

このような出力になれば成功。




PyROOTを使った方法

PyROOTはC++で書いたものよりも浸透していないので記事が少ない。
特に同じことをするためのコードを比較するような記事が少ないので是非確認してほしい。

作成の流れ
  1. データの準備
  2. コードの作成
  3. 実行
データ準備

C++のやり方と同じ方法で行う

コードの作成

今回は「mkNT.py」という名前で作成する。

from ROOT import TTree , std , TH1F, TFile
import numpy as np

kNameInFile = "output.dat"
kNameOutFile = "output.root"

print("Input File  = "+kNameInFile)
print("Output File = "+kNameOutFile)

# define value
ieve = np.empty((1), dtype="int32")  # この書き方でないとバグる
adc = np.empty((1), dtype="int32") # この書き方でないとバグる
tdc = np.zeros((3,), dtype="int32") # この書き方でないとバグる
rand = np.zeros((2,3), dtype="float") # この書き方でないとバグる

# open root
rawtree = TTree("raw","raw_tree")
rawtree.Branch('ieve',ieve,'ieve/I')
rawtree.Branch('adc',adc,'adc/I')
rawtree.Branch('tdc',tdc,'tdc[3]/I')
rawtree.Branch('rand',rand,'rand[2][3]/D')

# open dat file
fin = open(kNameInFile,"r")

# get data & wtite
for line in fin:
    tmp = line.split()
    if len(tmp)==0:
        continue
    ieve[0] = int(tmp[0])
    adc[0] = int(tmp[1])
    tdc[0] = int(tmp[2])
    tdc[1] = int(tmp[3])
    tdc[2] = int(tmp[4])
    rand[0][0] = float(tmp[5])
    rand[0][1] = float(tmp[6])
    rand[0][2] = float(tmp[7])
    rand[1][0] = float(tmp[8])
    rand[1][1] = float(tmp[9])
    rand[1][2] = float(tmp[10])
    rawtree.Fill()

    if ieve%1000 == 0:
        print("event_loop = "+str(ieve))
print("event num = "+str(ieve+1))
fin.close()

# write rootfile
fout = TFile(kNameOutFile,"recreate")
rawtree.Write()
fout.Close()
# end
print("F!!!!!!!!")

実行

Pythonの場合は簡単で、terminal上で以下のコマンドを入力するだけである。

[***@localhost **] $ python3 mkNT.py

Input File  = output.dat
Output File = output.root
event_loop = [0]
event_loop = [1500]
event_loop = [3000]
event_loop = [4500]
event_loop = [6000]
event_loop = [7500]
event_loop = [9000]
event num = [10000]
F!!!!!!!!

このような出力になれば成功。


確認

後は好きなように完成したrootファイルを確認すればよい。

[***@localhost **] $ root output.root
root [] .ls
root [] raw->Print()
root [] raw->Draw("neve")
root [] raw->Draw("adc")
root [] raw->Draw("tdc[0]")
root [] raw->Draw("rand[0][0]")
root [] raw->Draw("adc:tdc[0]")
root [] raw->Draw("adc:tdc[0]-tdc[1]-tdc[2]")

まとめ

C++ :記事が多いため、ROOTそのものを扱いやすい。
Python:PyROOTの記事は少ないが、Pythonなのでファイルを扱いやすいなどの研究活動にはおすすめ。