Dokumentasi dan pengemasan

Mendokumentasikan dan mengemas proyek fpm dengan build yang dapat direproduksi.

Daftar isi

Proyek yang sudah terverifikasi masih perlu dikemas agar dapat dibangun dan dijalankan orang lain tanpa panduan lisan. Topik ini mengemas poisson1d sebagai proyek fpm yang lengkap, terdiri atas manifes fpm.toml, README, program utama dengan argumen baris perintah, uji otomatis, dan perintah build yang dapat direproduksi. Pohon proyek disiapkan di luar jalur konten, dibangun dengan fpm, lalu dijalankan dan diuji sungguhan sehingga setiap keluaran pada topik ini adalah hasil eksekusi asli.

Pohon proyek dan manifes fpm#

fpm menetapkan konvensi direktori untuk sebuah proyek: pustaka berada di src/, program yang dapat dieksekusi di app/, dan program uji di test/. Ketiga direktori ini dikenali fpm tanpa perlu diumumkan di mana pun, sehingga letak berkas sekaligus menyatakan perannya. Bagi proyek sekecil poisson1d, tiga direktori itulah satu-satunya yang perlu disiapkan. Struktur berikut adalah pohon lengkap poisson1d beserta manifes dan dokumentasinya.

text Struktur pohon proyek poisson1d
poisson1d/
├── fpm.toml
├── README.md
├── src
│   └── poisson_mod.f90
├── app
│   └── main.f90
└── test
    └── uji_poisson.f90

Manifes fpm.toml adalah kontrak build proyek. Nama proyek sekaligus menjadi nama program yang dapat dieksekusi, sedangkan bagian [build] mengatur target yang dipindai fpm secara otomatis. Dengan kontrak ini, proyek dapat dibangun ulang di mesin lain tanpa pengaturan apa pun karena seluruh aturan build ikut tersimpan bersama kode sumber.

toml fpm.toml
name = "poisson1d"
version = "0.1.0"
license = "MIT"
description = "Penyelesai PDE Poisson 1D dengan metode beda hingga orde dua"

[build]
auto-executables = true
auto-tests = true
auto-examples = false

Empat kunci pertama menjelaskan proyek kepada registri dan pembaca manifes. Kunci name telah dijelaskan pada paragraf sebelumnya, kunci version dan license membuat setiap rilis dapat dirujuk secara unik dan dipakai ulang secara sah, sedangkan description merangkum fungsi proyek dalam satu baris bagi calon pengguna. Bagian [build] membiarkan fpm memindai src/, app/, dan test/ secara otomatis, dengan target examples dimatikan karena proyek ini tidak memiliki direktori example/. Dengan manifes sesempit ini, seluruh keputusan build proyek dapat dibaca dalam sekali pandang.

README dan dokumentasinya#

README adalah halaman pertama yang dibaca pengguna proyek, sehingga isinya harus menjawab tiga hal: apa yang dikerjakan program, bagaimana menjalankannya, dan bagaimana membangunnya. Untuk proyek CLI numerik, urutan itu sudah cukup, dan setiap perintah yang dicantumkan harus benar-benar pernah dijalankan. Susunan ringkas semacam ini juga mudah dipelihara, sebab setiap perubahan antarmuka cukup disunting pada beberapa baris dan langsung terbaca pengguna.

markdown README.md
# poisson1d

Penyelesai persamaan Poisson satu dimensi -u'' = f pada [0, 1] dengan
syarat batas Dirichlet homogen. Diskretisasi memakai beda hingga orde dua
dan sistem tridiagonalnya diselesaikan dengan algoritma Thomas.

## Pemakaian

    poisson1d [--n N] [--verifikasi] [--keluaran BERKAS]

## Membangun

    fpm build
    fpm run -- --n 63 --verifikasi
    fpm test

Dokumentasi di atas sengaja memuat perintah yang sama dengan yang dieksekusi pada bagian akhir topik ini, sehingga apa yang tertulis di README dan apa yang benar-benar berjalan tidak pernah menyimpang. Tiga perintah pada bagian Membangun dipetakan satu per satu: fpm build ke blok kompilasi proyek, fpm run ke blok eksekusi program, dan fpm test ke blok uji penutup. Bagian Pemakaian melengkapinya dengan ringkasan ketiga opsi baris perintah, sehingga pengguna dapat menyusun pemanggilannya sendiri tanpa membaca ulang seluruh topik. Pembaca yang menyalin ketiga perintah itu ke terminal menerima keluaran yang sama dengan yang tercetak pada topik ini.

Program utama dengan argumen baris perintah#

Program di app/main.f90 menjadi antarmuka proyek. Perulangan get_command_argument memindai argumen satu per satu, select case memetakan opsi --n, --verifikasi, dan --keluaran, sedangkan prosedur gagal mencetak pesan beserta pemakaian program lalu menghentikannya dengan stop 1. Nilai bawaan ditetapkan di awal program, yaitu n=63n = 63 tanpa verifikasi dan tanpa keluaran berkas, sehingga pemanggilan tanpa satu pun argumen tetap menghasilkan solusi yang sah.

fortran poisson1d.f90
program poisson1d
   use poisson_mod, only: dp, pi, jaring, buat_jaring, selesaikan, norma_inf, norma_l2
   implicit none
   type(jaring) :: g
   real(dp), allocatable :: f(:), u(:), u_eksak(:)
   real(dp) :: err_inf, err_l2
   character(len=64) :: arg, nama_keluaran
   integer :: n, argc, i, ios, unit_keluaran
   logical :: verifikasi

   ! nilai bawaan opsi baris perintah
   n = 63
   verifikasi = .false.
   nama_keluaran = ''

   argc = command_argument_count()
   i = 1
   do while (i <= argc)
      call get_command_argument(i, arg)
      select case (trim(arg))
      case ('--n')
         i = i + 1
         if (i > argc) call gagal('--n memerlukan nilai N')
         call get_command_argument(i, arg)
         read (arg, *, iostat=ios) n
         if (ios /= 0 .or. n < 1) call gagal('nilai --n tidak valid: '//trim(arg))
      case ('--verifikasi')
         verifikasi = .true.
      case ('--keluaran')
         i = i + 1
         if (i > argc) call gagal('--keluaran memerlukan nama berkas')
         call get_command_argument(i, nama_keluaran)
      case default
         call gagal('argumen tidak dikenal: '//trim(arg))
      end select
      i = i + 1
   end do

   g = buat_jaring(n)
   f = pi**2*sin(pi*g%x)
   call selesaikan(g, f, u)
   print '(A, I0, A, ES11.4)', 'n = ', n, ', h = ', g%h
   print '(A, F9.6, A, F7.4)', 'maksimum u   : ', maxval(u), ' pada x = ', &
        g%x(maxloc(u, dim=1) - 1)

   if (verifikasi) then
      u_eksak = sin(pi*g%x)
      err_inf = norma_inf(u, u_eksak)
      err_l2 = norma_l2(u, u_eksak, g%h)
      print '(A, ES12.5)', 'error L-inf  : ', err_inf
      print '(A, ES12.5)', 'error L2     : ', err_l2
   end if

   if (nama_keluaran /= '') then
      open (newunit=unit_keluaran, file=nama_keluaran, status='replace', action='write')
      write (unit_keluaran, '(A)') 'x,u,u_eksak'
      do i = 0, n + 1
         write (unit_keluaran, '(ES15.8, A, ES15.8, A, ES15.8)') &
              g%x(i), ',', u(i), ',', sin(pi*g%x(i))
      end do
      close (unit_keluaran)
      print '(A, A, A)', 'solusi ditulis ke ', trim(nama_keluaran), &
           ' (kolom: x, u, u_eksak)'
   end if

contains

   ! mencetak pesan error beserta pemakaian program, lalu menghentikan program
   subroutine gagal(pesan)
      character(len=*), intent(in) :: pesan

      print '(A, A)', 'error: ', pesan
      print '(A)', 'pemakaian: poisson1d [--n N] [--verifikasi] [--keluaran BERKAS]'
      stop 1
   end subroutine gagal

end program poisson1d

Opsi --keluaran menulis solusi sebagai berkas CSV dengan tiga kolom, yaitu koordinat xx, solusi numerik, dan solusi eksak. Berkas semacam itu dipakai untuk pemeriksaan lanjutan di luar program, misalnya digambar dengan alat lain. Penomoran unit berkas diserahkan ke newunit agar program aman dipanggil berulang tanpa menabrak unit yang sudah terbuka.

Uji otomatis dengan fpm test#

Program uji di test/ dijalankan fpm melalui perintah fpm test. Uji ini menyelesaikan masalah dengan n=127n = 127, membandingkan hasilnya dengan solusi eksak, dan menyatakan gagal melalui stop 1 apabila error melampaui ambang 1.0e-3. Pemeriksaan sengaja memakai norma L-inf saja karena error terburuk itulah yang paling tajam membongkar kerusakan pada satu titik akibat regresi.

fortran uji_poisson.f90
program uji_poisson
   use poisson_mod, only: dp, pi, jaring, buat_jaring, selesaikan, norma_inf
   implicit none
   type(jaring) :: g
   real(dp), allocatable :: f(:), u(:), u_eksak(:)
   real(dp) :: err

   ! uji: solusi eksak sin(pi x) direproduksi dalam batas toleransi
   g = buat_jaring(127)
   f = pi**2*sin(pi*g%x)
   call selesaikan(g, f, u)
   u_eksak = sin(pi*g%x)
   err = norma_inf(u, u_eksak)
   if (err > 1.0e-3_dp) then
      print '(A, ES12.5)', 'uji poisson1d GAGAL, error L-inf = ', err
      stop 1
   end if
   print '(A, ES12.5)', 'uji poisson1d: LOLOS, error L-inf = ', err
end program uji_poisson

Ambang ditetapkan jauh di atas error terukur, yang pada n=127n = 127 bernilai 5.020092E-05 menurut tabel konvergensi. Selama perakit sistem dan penyelesainya tidak diubah, error aktual akan tetap jauh di bawah ambang itu, sehingga uji hanya gagal ketika muncul regresi yang sungguh merusak jawaban. Kode keluar 1 pada kegagalan membuat uji ini dapat dipasang pada alat integrasi berkelanjutan tanpa penyesuaian apa pun.

Membangun dan menjalankan dengan fpm#

Seluruh rangkaian berikut dijalankan dari direktori yang memuat ketiga berkas sumber. Pohon proyek dibangun di /tmp sehingga jalur konten tetap bersih dari artefak. Perintah cp menempatkan tiap berkas sumber sesuai konvensi fpm, sedangkan dua heredoc menulis fpm.toml dan README.md dengan isi yang sama persis dengan blok di atas.

bash
# menyiapkan pohon proyek fpm di luar jalur konten
PROYEK=/tmp/poisson1d
rm -rf "$PROYEK"
mkdir -p "$PROYEK/src" "$PROYEK/app" "$PROYEK/test"
cp poisson_mod.f90 "$PROYEK/src/poisson_mod.f90"
cp poisson1d.f90 "$PROYEK/app/main.f90"
cp uji_poisson.f90 "$PROYEK/test/uji_poisson.f90"
cd "$PROYEK"
# menulis manifes sesuai blok fpm.toml di atas
cat > fpm.toml <<'EOF'
name = "poisson1d"
version = "0.1.0"
license = "MIT"
description = "Penyelesai PDE Poisson 1D dengan metode beda hingga orde dua"

[build]
auto-executables = true
auto-tests = true
auto-examples = false
EOF
# menulis README sesuai blok README.md di atas
cat > README.md <<'EOF'
# poisson1d

Penyelesai persamaan Poisson satu dimensi -u'' = f pada [0, 1] dengan
syarat batas Dirichlet homogen. Diskretisasi memakai beda hingga orde dua
dan sistem tridiagonalnya diselesaikan dengan algoritma Thomas.

## Pemakaian

    poisson1d [--n N] [--verifikasi] [--keluaran BERKAS]

## Membangun

    fpm build
    fpm run -- --n 63 --verifikasi
    fpm test
EOF
# mengaktifkan peringatan kompiler lalu membangun proyek
export FPM_FFLAGS="-Wall -Wextra"
fpm build
text Keluaran
 + mkdir -p build/dependencies
[  0%]                poisson_mod.f90
[ 25%]                poisson_mod.f90  done.
[ 25%]                 libpoisson1d.a
[ 25%]                       main.f90
[ 50%]                 libpoisson1d.a  done.
[ 75%]                       main.f90  done.
[ 75%]                      poisson1d
[100%]                      poisson1d  done.
[100%] Project compiled successfully.

Build pertama mengompilasi pustaka dari src/, kemudian program dari app/, lalu menyambung keduanya menjadi eksekusi bernama poisson1d sesuai name pada manifes. Persentase kemajuan pada keluaran menandai target yang selesai satu per satu, dari poisson_mod.f90, pustaka libpoisson1d.a, main.f90, hingga eksekusi poisson1d. Program kini dijalankan dengan verifikasi dan keluaran berkas.

bash
# menjalankan program dengan verifikasi dan keluaran CSV
fpm run -- --n 63 --verifikasi --keluaran solusi.csv
# menampilkan empat baris pertama berkas solusi
head -4 solusi.csv
text Keluaran
Project is up to date
n = 63, h =  1.5625E-02
maksimum u   :  1.000201 pada x =  0.5000
error L-inf  :  2.00822E-04
error L2     :  1.42002E-04
solusi ditulis ke solusi.csv (kolom: x, u, u_eksak)
x,u,u_eksak
 0.00000000E+00, 0.00000000E+00, 0.00000000E+00
 1.56250000E-02, 4.90775282E-02, 4.90676743E-02
 3.12500000E-02, 9.80368243E-02, 9.80171403E-02

Error yang dilaporkan program, yaitu 2.00822E-04, sama dengan baris n=63n = 63 pada tabel konvergensi, sehingga program terkemas dan program studi menghasilkan angka yang konsisten. Tanda -- pada fpm run memisahkan argumen fpm dari argumen program, dan head menampilkan bahwa CSV berisi kepala kolom beserta pasangan nilai numerik dan eksak yang berdekatan. Uji otomatis dijalankan terakhir.

bash
# menjalankan uji otomatis fpm
fpm test
text Keluaran
[  0%]                uji_poisson.f90
[ 50%]                uji_poisson.f90  done.
[ 50%]                    uji_poisson
[100%]                    uji_poisson  done.
[100%] Project compiled successfully.
uji poisson1d: LOLOS, error L-inf =  5.02009E-05

Uji berjalan setelah target ujinya dikompilasi dan mengembalikan kode keluar 0 ketika lolos. Pencarian program uji oleh fpm pun minim pengaturan karena kunci auto-tests = true pada manifes membuat fpm memindai dan menjalankan program uji di test/ secara otomatis. Prosedur gagal pada program utama tetap berperan sebagai pengaman: setiap argumen yang tidak valid menghentikan program dengan kode keluar 1 sebelum perhitungan dimulai, sehingga pemanggil skrip dapat mendeteksi kegagalan dari kode keluar tersebut.

Kesalahan umum#

  • Argumen program diteruskan tanpa --. Perintah fpm run --n 63 memberi --n kepada fpm, bukan kepada program, dan fpm menolaknya sebagai opsi yang tidak dikenal. Perbaikannya dengan menyisipkan -- sebelum argumen pertama program.
  • Staging yang tidak dibersihkan. Menyalin sumber ke pohon proyek lama tanpa rm -rf menyisakan berkas versi sebelumnya, sehingga build tidak mencerminkan sumber terkini. Blok penyiapan pada topik ini selalu menghapus pohon target lebih dahulu.
  • Nama program diasumsikan bebas. Program dari app/main.f90 mewarisi nama proyek pada manifes. Mengganti name tanpa memperbarui README dan skrip pemanggil membuat perintah dokumentasi gagal dijalankan.

Ringkasan#

  • Proyek dikemas sebagai pohon fpm standar dengan manifes fpm.toml, README, pustaka di src/, program CLI di app/, dan uji di test/.
  • Flag kompiler diteruskan melalui FPM_FFLAGS, sedangkan argumen program dipisahkan dari argumen fpm dengan --.
  • fpm build, fpm run, dan fpm test dijalankan sungguhan, dan error program konsisten dengan tabel konvergensi pada verifikasi kebenaran.
  • Kode keluar 1 pada kegagalan program dan uji membuat pengemasan ini siap dipasang pada alat integrasi berkelanjutan.

Referensi#