オンライン 平均自由行程計算機(Mean Free Path Calculator)

真空工学
Hannes Grobe, CC BY 3.0 , via Wikimedia Commons

ガス分子を選択し(または分子直径を直接入力)、圧力、温度、代表寸法を入力すると平均自由行程とクヌーセン数をオンラインで計算してくれる JavaScript プログラムを ChatGPT に作ってもらいました。

平均自由行程とクヌーセン数計算機

今回ChatGPTに「WordPress上でオンライン計算ができるページの作り方」自体を聞くことからはじめています。当然 JavaScript を使ったこともなく、ChatGPT に指示するだけでコードは1文字も書いていません。ChatGPT が作ってくれたコードを WordPress のカスタムHTMLブロックに貼り付けて動作確認と検証を行い、レイアウトの修正なども全て ChatGPT への指示を繰り返して行いました。下にあるのが ChatGPT が書き出したコードで、これを埋め込んだものが上の計算ツールになります。作成時間は2,3時間、HTML + CSS + JavaScript の事前知識があればもっと短時間で済んだでしょう。改めて生成AIのすごさを実感しました。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>平均自由行程とクヌーセン数計算機</title>
  <style>
    .mean-free-path-form {
      max-width: 650px;
      margin: auto;
      padding: 1em;
      border: 1px solid #ccc;
      border-radius: 10px;
      font-family: sans-serif;
    }

    .form-row {
      display: flex;
      align-items: center;
      margin-top: 1em;
    }

    .form-row label {
      flex: 0 0 30%;
      font-weight: bold;
      text-align: left;
    }

    .input-group {
      display: flex;
      flex: 1 0 70%;
      align-items: center;
    }

    input.input-main,
    select.unit-select {
      padding: 0.5em;
      font-size: 1em;
    }

    input.input-main {
      flex: 1 0 50%;
    }

    select.unit-select {
      flex: 0 0 20%;
      margin-left: 0.5em;
    }

    .result-output {
      flex: 1 0 50%;
      padding: 0.5em;
      font-size: 1em;
      background: #f8f8f8;
      border: 1px solid #ddd;
      border-radius: 4px;
      line-height: 1.2em;
      text-align: left;
      min-height: 38px;
      box-sizing: border-box;
    }

    .knudsen-flow {
      flex: 0 0 40%;
      font-weight: bold;
      padding-left: 1em;
    }
  </style>
</head>
<body>
<div class="mean-free-path-form">
  <!-- ガス分子 -->
  <div class="form-row">
    <label for="gas">ガス分子</label>
    <div class="input-group">
      <select id="gas" class="input-main" onchange="setDiameter();">
        <option value="" disabled selected style="color:blue;">Select</option>
        <option value="H2">H₂</option>
        <option value="He">He</option>
        <option value="CH4">CH₄</option>
        <option value="NH3">NH₃</option>
        <option value="H2O">H₂O</option>
        <option value="Ne">Ne</option>
        <option value="C2H2">C₂H₂</option>
        <option value="N2">N₂</option>
        <option value="CO">CO</option>
        <option value="C2H4">C₂H₄</option>
        <option value="NO">NO</option>
        <option value="O2">O₂</option>
        <option value="H2S">H₂S</option>
        <option value="HCl">HCl</option>
        <option value="Ar">Ar</option>
        <option value="C3H6">C₃H₆</option>
        <option value="CO2">CO₂</option>
        <option value="N2O">N₂O</option>
        <option value="C3H8">C₃H₈</option>
        <option value="SO2">SO₂</option>
        <option value="Cl2">Cl₂</option>
        <option value="C6H6">C₆H₆</option>
        <option value="HBr">HBr</option>
        <option value="Kr">Kr</option>
        <option value="Xe">Xe</option>
        <option value="SF6">SF₆</option>
        <option value="CCl4">CCl₄</option>
        <option value="Br2">Br₂</option>
      </select>
      <div class="unit-select"></div>
    </div>
  </div>

  <!-- 分子直径 d -->
  <div class="form-row">
    <label>分子直径 d</label>
    <div class="input-group">
      <input type="number" id="diameter" class="input-main" step="0.1" value="">
      <select class="unit-select" id="diameterUnit" onchange="convertInput('diameter')">
        <option value="pm" selected>pm</option>
        <option value="nm">nm</option>
        <option value="um">μm</option>
        <option value="mm">mm</option>
        <option value="m">m</option>
      </select>
    </div>
  </div>

  <!-- 圧力 p -->
  <div class="form-row">
    <label>圧力 p</label>
    <div class="input-group">
      <input type="number" id="pressure" class="input-main" value="" step="1">
      <select class="unit-select" id="pressureUnit" onchange="convertInput('pressure')">
        <option value="Pa" selected>Pa</option>
        <option value="atm">atm</option>
        <option value="mTorr">mTorr</option>
      </select>
    </div>
  </div>

  <!-- 温度 T -->
  <div class="form-row">
    <label>温度 T</label>
    <div class="input-group">
      <input type="number" id="temperature" class="input-main" value="" step="0.1">
      <select class="unit-select" id="temperatureUnit" onchange="convertInput('temperature')">
        <option value="K" selected>K</option>
        <option value="C">℃</option>
      </select>
    </div>
  </div>

  <!-- 平均自由行程 λ -->
  <div class="form-row">
    <label>平均自由行程 λ</label>
    <div class="input-group">
      <div id="result" class="result-output">---</div>
      <select class="unit-select" id="resultUnit" onchange="calculate()">
        <option value="pm">pm</option>
        <option value="nm">nm</option>
        <option value="um">μm</option>
        <option value="mm" selected>mm</option>
        <option value="m">m</option>
      </select>
    </div>
  </div>

  <!-- 代表寸法 L -->
  <div class="form-row">
    <label>代表寸法 L</label>
    <div class="input-group">
      <input type="number" id="characteristicLength" class="input-main" value="" step="0.001">
      <select class="unit-select" id="lengthUnit" onchange="convertInput('length')">
        <option value="pm">pm</option>
        <option value="nm">nm</option>
        <option value="um">μm</option>
        <option value="mm" selected>mm</option>
        <option value="m">m</option>
      </select>
    </div>
  </div>

  <!-- クヌーセン数 Kn + 区分 -->
  <div class="form-row">
    <label>クヌーセン数 Kn</label>
    <div class="input-group">
      <div id="knudsenResult" class="result-output" style="flex:1;">---</div>
      <div id="flowRegime" class="knudsen-flow">---</div>
    </div>
  </div>
</div>

<script>
  const diameters = {
    H2: 289, He: 260, CH4: 380, NH3: 260, H2O: 265, Ne: 275, C2H2: 330, N2: 364, CO: 376, C2H4: 390,
    NO: 317, O2: 346, H2S: 360, HCl: 320, Ar: 340, C3H6: 450, CO2: 330, N2O: 330, C3H8: 430, SO2: 360,
    Cl2: 320, C6H6: 585, HBr: 350, Kr: 360, Xe: 396, SF6: 550, CCl4: 590, Br2: 350
  };

  const unitFactors = {
    diameter: { pm: 1e-12, nm: 1e-9, um: 1e-6, mm: 1e-3, m: 1 },
    pressure: { Pa: 1, atm: 101325, mTorr: 101325 / 760000 },
    temperature: {
      toK: (val, unit) => unit === 'K' ? val : val + 273.15,
      fromK: (val, unit) => unit === 'K' ? val : val - 273.15
    },
    result: { pm: 1e12, nm: 1e9, um: 1e6, mm: 1e3, m: 1 },
    length: { pm: 1e-12, nm: 1e-9, um: 1e-6, mm: 1e-3, m: 1 }
  };

  const previousUnits = {
    diameter: 'pm', pressure: 'Pa', temperature: 'K', length: 'mm'
  };

  function setDiameter() {
    const gasSelect = document.getElementById('gas');
    const gas = gasSelect.value;
    if (gas && diameters.hasOwnProperty(gas)) {
      document.getElementById('diameter').value = diameters[gas];
      document.getElementById('diameterUnit').value = 'pm';
      previousUnits['diameter'] = 'pm';
    } else {
      document.getElementById('diameter').value = '';
    }
    calculate();
  }

  function convertInput(type) {
    const input = document.getElementById(type === 'length' ? 'characteristicLength' : type);
    const unitSelect = document.getElementById(type + 'Unit');
    const oldUnit = previousUnits[type];
    const newUnit = unitSelect.value;
    let val = parseFloat(input.value);
    if (isNaN(val)) return;

    if (type === 'temperature') {
      const valInK = unitFactors.temperature.toK(val, oldUnit);
      const newVal = unitFactors.temperature.fromK(valInK, newUnit);
      input.value = newVal.toFixed(2);
    } else if (type === 'pressure') {
      // 圧力単位変換は1 atm=101325 Pa=760000 mTorr を基準に換算
      // 入力値を旧単位からPaに変換してから新単位に戻す
      const valInPa = val * unitFactors.pressure[oldUnit];
      const converted = valInPa / unitFactors.pressure[newUnit];
      input.value = formatSmart(converted);
    } else {
      const base = val * unitFactors[type][oldUnit];
      const converted = base / unitFactors[type][newUnit];
      input.value = formatSmart(converted);
    }

    previousUnits[type] = newUnit;
    calculate();
  }

  function formatSmart(val) {
    if (val === 0) return '0';
    const absVal = Math.abs(val);
    if (absVal < 1e-4 || absVal >= 1e6) {
      return val.toExponential(1);
    } else {
      return val.toFixed(6).replace(/\.?0+$/, '');
    }
  }

  function calculate() {
    const k = 1.380649e-23;
    const pi = 3.141592653589793;

    const d = parseFloat(document.getElementById('diameter').value) * unitFactors.diameter[document.getElementById('diameterUnit').value];
    const P = parseFloat(document.getElementById('pressure').value) * unitFactors.pressure[document.getElementById('pressureUnit').value];
    const T = unitFactors.temperature.toK(parseFloat(document.getElementById('temperature').value), document.getElementById('temperatureUnit').value);
    const lengthInput = document.getElementById('characteristicLength').value;
    const L = lengthInput ? parseFloat(lengthInput) * unitFactors.length[document.getElementById('lengthUnit').value] : null;
    const resultUnit = document.getElementById('resultUnit').value;

    if ([d, P, T].some(v => isNaN(v) || v <= 0)) {
      document.getElementById('result').textContent = "---";
      document.getElementById('knudsenResult').textContent = "---";
      document.getElementById('flowRegime').textContent = "---";
      return;
    }

    const lambda = k * T / (Math.sqrt(2) * pi * d * d * P);
    const lambdaConv = lambda * unitFactors.result[resultUnit];
    document.getElementById('result').textContent = formatSmart(lambdaConv);

    if (!L || L <= 0) {
      document.getElementById('knudsenResult').textContent = "---";
      document.getElementById('flowRegime').textContent = "---";
      return;
    }

    const Kn = lambda / L;
    document.getElementById('knudsenResult').textContent = formatSmart(Kn);

    const flowRegime = document.getElementById('flowRegime');
    if (Kn < 0.01) {
      flowRegime.textContent = "連続流";
      flowRegime.style.color = "blue";
    } else if (Kn < 0.1) {
      flowRegime.textContent = "すべり流";
      flowRegime.style.color = "green";
    } else if (Kn < 10) {
      flowRegime.textContent = "遷移流";
      flowRegime.style.color = "brown";
    } else {
      flowRegime.textContent = "自由分子流";
      flowRegime.style.color = "red";
    }
  }

  document.querySelectorAll('input, select').forEach(el => {
    if (el.id !== 'resultUnit') {
      el.addEventListener('input', calculate);
      el.addEventListener('change', () => {
        if (el.tagName === 'SELECT') calculate();
      });
    }
  });

  window.addEventListener('DOMContentLoaded', () => {
    setDiameter();
    calculate();
  });
</script>
</body>
</html>

コメント