オンライン(圧力・流量)単位変換計算機(Pressure and Flow Rate Unit Converter)

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

圧力と流量の単位変換を行う(HTML+CSS+JavaScript)プログラムを、オンライン平均自由行程計算機 に続いて ChatGPT に作ってもらいました。

圧力単位変換計算機
圧力単位変換計算機(Pressure Unit Converter)

流量単位変換計算機
流量単位変換計算機(Flow Rate Unit Converter)

単位変換の計算部分自体はすぐに出来たのですが、モバイル端末で見たときの各ボックスのレイアウトの変更(CSSのレスポンシブ対応)の指示が ChatGPT になかなかうまく伝わらず少し大変でした。指示の出し方が悪いのもしれませんが、一度直したところをまた元に戻されたりします。ソースコードはそれぞれ以下の通りです(自分の勉強用に貼り付けます)。

圧力単位変換のソースコード
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>圧力単位変換計算機</title>
  <style>
    .pressure-converter {
      font-family: sans-serif;
      max-width: 900px;
      margin: 40px auto;
      padding: 50px 20px 30px 20px;
      border: 2px solid #777;
      border-radius: 15px;
      box-shadow: 0 4px 8px rgba(0, 0, 0, 0.12);
      background-color: #fafafa;
      position: relative;
      box-sizing: border-box;
      overflow-x: hidden;
    }

    .pressure-converter-title {
      position: absolute;
      top: 12px;
      left: 20px;
      right: 20px;
      font-weight: bold;
      font-size: 1.2em;
      background-color: #fafafa;
      padding: 0 8px;
      color: #333;
      word-break: keep-all;
      white-space: normal;
    }

    .pressure-units-row {
      display: flex;
      flex-wrap: wrap;
      gap: 12px;
      justify-content: space-between;
      margin-top: 25px;
      padding-top: 25px;
    }

    .pressure-unit {
      display: flex;
      flex-direction: column;
      align-items: center;
      flex: 1 1 18%;
      min-width: 100px;
      max-width: 180px;
      box-sizing: border-box;
    }

    .pressure-unit label {
      margin-bottom: 6px;
      font-weight: bold;
    }

    .pressure-unit input {
      width: 100%;
      padding: 6px 8px;
      text-align: right;
      font-size: 1em;
      border: 1px solid #ccc;
      border-radius: 6px;
      transition: border-color 0.3s ease;
    }

    .pressure-unit input:focus {
      border-color: #0066cc;
      outline: none;
    }

    @media (max-width: 600px) {
      .pressure-unit {
        flex: 1 1 calc(48% - 6px);
        max-width: calc(48% - 6px);
      }
    }
  </style>
</head>
<body>
  <div class="pressure-converter" role="region" aria-label="圧力単位変換計算機">
    <div class="pressure-converter-title">圧力単位変換計算機(Pressure Unit Converter)</div>
    <div class="pressure-units-row">
      <div class="pressure-unit">
        <label for="pressure-pa">Pa</label>
        <input type="number" id="pressure-pa" min="0" step="any" oninput="convertPressureFrom('pa')" />
      </div>
      <div class="pressure-unit">
        <label for="pressure-mbar">mbar (hPa)</label>
        <input type="number" id="pressure-mbar" min="0" step="any" oninput="convertPressureFrom('mbar')" />
      </div>
      <div class="pressure-unit">
        <label for="pressure-mtorr">mTorr</label>
        <input type="number" id="pressure-mtorr" min="0" step="any" oninput="convertPressureFrom('mtorr')" />
      </div>
      <div class="pressure-unit">
        <label for="pressure-torr">Torr</label>
        <input type="number" id="pressure-torr" min="0" step="any" oninput="convertPressureFrom('torr')" />
      </div>
      <div class="pressure-unit">
        <label for="pressure-atm">atm</label>
        <input type="number" id="pressure-atm" min="0" step="any" oninput="convertPressureFrom('atm')" />
      </div>
    </div>
  </div>

  <script>
    const PA_ATM = 101325;
    const TORR_ATM = 760;
    let pressureUpdating = false;

    function convertPressureFrom(unit) {
      if (pressureUpdating) return;
      pressureUpdating = true;

      const inputs = {
        pa: document.getElementById("pressure-pa"),
        mbar: document.getElementById("pressure-mbar"),
        mtorr: document.getElementById("pressure-mtorr"),
        torr: document.getElementById("pressure-torr"),
        atm: document.getElementById("pressure-atm"),
      };

      let val = parseFloat(inputs[unit].value);
      if (isNaN(val) || val < 0) {
        clearPressureFields(unit);
        pressureUpdating = false;
        return;
      }

      let pa;
      switch (unit) {
        case "pa":
          pa = val;
          break;
        case "mbar":
          pa = val * 100;
          break;
        case "mtorr":
          pa = (val / 1000) * (PA_ATM / TORR_ATM);
          break;
        case "torr":
          pa = val * (PA_ATM / TORR_ATM);
          break;
        case "atm":
          pa = val * PA_ATM;
          break;
      }

      inputs.pa.value = formatPressure(pa);
      inputs.mbar.value = formatPressure(pa / 100);
      inputs.mtorr.value = formatPressure((pa / PA_ATM) * TORR_ATM * 1000);
      inputs.torr.value = formatPressure((pa / PA_ATM) * TORR_ATM);
      inputs.atm.value = formatPressure(pa / PA_ATM);

      pressureUpdating = false;
    }

    function clearPressureFields(except = "") {
      const ids = ["pa", "mbar", "mtorr", "torr", "atm"];
      for (const id of ids) {
        if (id !== except) {
          document.getElementById("pressure-" + id).value = "";
        }
      }
    }

    function formatPressure(val) {
      if (val === 0) return "0";
      if (Math.abs(val) >= 1e6 || Math.abs(val) < 1e-4) {
        return val.toExponential(5);
      } else {
        return parseFloat(val.toFixed(6));
      }
    }
  </script>
</body>
</html>
流量単位変換のソースコード
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>流量単位変換計算機</title>
  <style>
    .flow-converter {
      font-family: sans-serif;
      max-width: 1100px;
      margin: 40px auto;
      padding: 60px 30px 30px;
      border: 2px solid #777;
      border-radius: 15px;
      box-shadow: 0 4px 8px rgba(0,0,0,0.12);
      background-color: #fafafa;
      position: relative;
      box-sizing: border-box;
    }

    .flow-converter-title {
      position: absolute;
      top: 12px;
      left: 20px;
      right: 20px;
      font-weight: bold;
      font-size: 1.2em;
      background-color: #fafafa;
      padding: 0 8px;
      color: #333;
      text-align: left;
    }

    .gas-select-row {
      display: flex;
      flex-wrap: wrap;
      gap: 30px;
      margin-top: 40px;
      margin-bottom: 20px;
      justify-content: flex-start;
    }

    .gas-column {
      display: flex;
      flex-direction: column;
      align-items: center;
      flex: 1 1 130px;
      min-width: 130px;
      max-width: 180px;
    }

    .gas-column label {
      font-weight: bold;
      margin-bottom: 5px;
      text-align: center;
    }

    .gas-column select,
    .gas-column input {
      padding: 6px 8px;
      font-size: 1em;
      width: 100%;
      box-sizing: border-box;
    }

    .flow-units-row {
      display: flex;
      flex-wrap: wrap;
      gap: 12px;
      justify-content: flex-start;
    }

    .flow-unit {
      display: flex;
      flex-direction: column;
      align-items: center;
      flex: 1 1 130px;
      min-width: 130px;
      max-width: 180px;
    }

    .flow-unit label {
      margin-bottom: 6px;
      font-weight: bold;
      text-align: center;
    }

    .flow-unit input {
      width: 100%;
      padding: 6px 8px;
      text-align: right;
      font-size: 1em;
      border: 1px solid #ccc;
      border-radius: 6px;
      box-sizing: border-box;
    }

    .flow-unit input:focus {
      border-color: #0066cc;
      outline: none;
    }

    @media (max-width: 600px) {
      .flow-unit,
      .gas-column {
        flex: 1 1 calc(48% - 6px);
        max-width: calc(48% - 6px);
      }
    }
  </style>
</head>
<body>
  <div class="flow-converter" role="region" aria-label="流量単位変換計算機">
    <div class="flow-converter-title">流量単位変換計算機(Flow Rate Unit Converter)</div>
    <div class="gas-select-row">
      <div class="gas-column">
        <label for="gas-select">ガス分子</label>
        <select id="gas-select" onchange="updateGas()">
          <option value="">-- 選択 --</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>
      <div class="gas-column">
        <label for="gas-molar-mass">分子量 (g/mol)</label>
        <input type="number" id="gas-molar-mass" min="0" step="any" oninput="updateMolarMassFromInput()">
      </div>
    </div>

    <div class="flow-units-row">
      <div class="flow-unit">
        <label for="flow-sccm">sccm</label>
        <input id="flow-sccm" type="number" min="0" step="any" oninput="convertFlowFrom('sccm')">
      </div>
      <div class="flow-unit">
        <label for="flow-pam3s">Pa·m³/s</label>
        <input id="flow-pam3s" type="number" min="0" step="any" oninput="convertFlowFrom('pam3s')">
      </div>
      <div class="flow-unit">
        <label for="flow-mbarls">mbar·L/s</label>
        <input id="flow-mbarls" type="number" min="0" step="any" oninput="convertFlowFrom('mbarls')">
      </div>
      <div class="flow-unit">
        <label for="flow-molps">molecule/s</label>
        <input id="flow-molps" type="number" min="0" step="any" oninput="convertFlowFrom('molps')">
      </div>
      <div class="flow-unit">
        <label for="flow-kgps">kg/s</label>
        <input id="flow-kgps" type="number" min="0" step="any" oninput="convertFlowFrom('kgps')">
      </div>
    </div>
  </div>
  <script>
    const kB = 1.380649e-23;
    const R = 8.31446261815324;
    const T0 = 273.15;
    const P0 = 101325;

    const molarMasses = {
      H2: 2.01588, He: 4.002602, CH4: 16.043, NH3: 17.031, H2O: 18.01528,
      Ne: 20.1797, C2H2: 26.038, N2: 28.0134, CO: 28.01, C2H4: 28.054,
      NO: 30.006, O2: 31.9988, H2S: 34.08, HCl: 36.46, Ar: 39.948,
      C3H6: 42.08, CO2: 44.0095, N2O: 44.0128, C3H8: 44.095, SO2: 64.066,
      Cl2: 70.906, C6H6: 78.1118, HBr: 80.912, Kr: 83.798,
      Xe: 131.293, SF6: 146.06, CCl4: 153.82, Br2: 159.808
    };

    let currentMolarMass = null;
    let flowUpdating = false;

    function updateGas() {
      const gas = document.getElementById("gas-select").value;
      if (molarMasses[gas]) {
        document.getElementById("gas-molar-mass").value = molarMasses[gas];
        currentMolarMass = molarMasses[gas] / 1000;
      } else {
        document.getElementById("gas-molar-mass").value = "";
        currentMolarMass = null;
      }
      updateKgpsOnly();
    }

    function updateMolarMassFromInput() {
      const val = parseFloat(document.getElementById("gas-molar-mass").value);
      if (!isNaN(val) && val > 0) {
        currentMolarMass = val / 1000;
        updateKgpsOnly();
      }
    }

    function updateKgpsOnly() {
      const sccm = parseFloat(document.getElementById("flow-sccm").value);
      if (!isNaN(sccm) && sccm >= 0 && currentMolarMass) {
        const kgps = sccm * (P0 / (R * T0)) * (1e-6 / 60) * currentMolarMass;
        document.getElementById("flow-kgps").value = formatValue(kgps);
      }
    }

    function convertFlowFrom(source) {
      if (flowUpdating) return;
      flowUpdating = true;

      const ids = {
        sccm: "flow-sccm",
        pam3s: "flow-pam3s",
        mbarls: "flow-mbarls",
        molps: "flow-molps",
        kgps: "flow-kgps"
      };

      const input = parseFloat(document.getElementById(ids[source]).value);
      if (isNaN(input) || input < 0) {
        clearFlowFields(source);
        flowUpdating = false;
        return;
      }

      let sccm;
      switch (source) {
        case "sccm": sccm = input; break;
        case "pam3s": sccm = input / (P0 * (1e-6 / 60)); break;
        case "mbarls": sccm = (input / 10) / (P0 * (1e-6 / 60)); break;
        case "molps": sccm = input / ((P0 / (kB * T0)) * (1e-6 / 60)); break;
        case "kgps":
          if (currentMolarMass) {
            sccm = input / ((P0 / (R * T0)) * (1e-6 / 60) * currentMolarMass);
          } else {
            alert("分子量を入力してください。");
            flowUpdating = false;
            return;
          }
          break;
      }

      const pam3s = sccm * P0 * (1e-6 / 60);
      const mbarls = pam3s * 10;
      const molps = sccm * (P0 / (kB * T0)) * (1e-6 / 60);
      const kgps = currentMolarMass ? sccm * (P0 / (R * T0)) * (1e-6 / 60) * currentMolarMass : null;

      document.getElementById("flow-sccm").value = formatValue(sccm);
      document.getElementById("flow-pam3s").value = formatValue(pam3s);
      document.getElementById("flow-mbarls").value = formatValue(mbarls);
      document.getElementById("flow-molps").value = formatValue(molps);
      document.getElementById("flow-kgps").value = kgps !== null ? formatValue(kgps) : "";

      flowUpdating = false;
    }

    function clearFlowFields(except = "") {
      const ids = ["sccm", "pam3s", "mbarls", "molps", "kgps"];
      for (const id of ids) {
        if (id !== except) {
          document.getElementById("flow-" + id).value = "";
        }
      }
    }

    function formatValue(val) {
      if (val === 0) return "0";
      if (Math.abs(val) >= 1e6 || Math.abs(val) < 1e-4) {
        return val.toExponential(5);
      } else {
        return parseFloat(val.toFixed(8));
      }
    }
  </script>
</body>
</html>

コメント