|
<!DOCTYPE html> |
|
<html lang="ja"> |
|
<head> |
|
<meta charset="utf-8"> |
|
<title>放射性物質の体内残留量グラフ</title> |
|
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> |
|
<style> |
|
.axis path { |
|
fill: none; |
|
stroke: black; |
|
} |
|
|
|
.axis line { |
|
stroke: black; |
|
} |
|
|
|
.line { |
|
fill: none; |
|
stroke: steelblue; |
|
stroke-width: 2; |
|
} |
|
|
|
.grid line{ |
|
stroke: lightgrey; |
|
} |
|
|
|
.grid path { |
|
fill: none; |
|
stroke: none; |
|
} |
|
|
|
input[type="text"] { |
|
width: 3em; |
|
} |
|
|
|
input[type="number"] { |
|
width: 5em; |
|
} |
|
|
|
#graph { |
|
float: left; |
|
width: 640px; |
|
height: 500px; |
|
} |
|
|
|
#sidePanel { |
|
float: left; |
|
width: 320px; |
|
height: 500px; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<div id="graph"> |
|
<svg></svg> |
|
</div> |
|
<div id="sidePanel"> |
|
<div id="control"> |
|
<form> |
|
<div> |
|
モデルを選択: <select id="model"></select><br> |
|
<span id="subjectLine">対象を選択: <select id="subject"></select></span> |
|
<span id="halflifeLine">生物学的半減期: <input type="number" value="110" id="biologicalHalflife" min="1">日</span> |
|
体重: <input type="number" value="70" id="weight" min="0">kg<br> |
|
1日の尿量: <input type="number" value="1.6" id="urineVolume" min="0" step="0.1">L/日<br> |
|
<br> |
|
核種: |
|
<select id="nuclide"> |
|
<option value="Cs137">セシウム137</option> |
|
<option value="Cs134">セシウム134</option> |
|
</select><br> |
|
物理学的半減期: <input type="number" value="30.17" id="physicalHalflife" min="0">年<br> |
|
</div> |
|
|
|
<div id="intake"> |
|
初期摂取: <input type="number" value="0" id="initial" min="0">Bq<br> |
|
慢性摂取: <br> |
|
<input type="number" value="0" id="start" min="0">日目から<input type="number" value="1000" id="end" min="0">日目まで<br> |
|
<input type="number" value="1" id="interval" min="0">日ごとに<input type="number" value="1" id="chronic" min="0">Bqずつ<br> |
|
</div> |
|
<div id="axis"> |
|
時間軸: <input type="number" value="1000" id="maxTime" min="1">日後まで表示<br> |
|
縦軸: <select id="quantity"></select>を表示 |
|
</div> |
|
</form> |
|
</div><!-- control --> |
|
|
|
<div id="result"> |
|
<p id="total"></p> |
|
<p id="steady"></p> |
|
<p>体内K-40量: <strong><span id="k40">0</span> Bq</strong></p> |
|
</div> |
|
|
|
</div><!-- sidePanel --> |
|
<script> |
|
(function() { |
|
|
|
// 科学的表記のメソッドを追加 |
|
Number.prototype.toScientificNotation = function(x) { |
|
return this.toPrecision(x).replace("e", "×10<sup>").replace("+", "") + "</sup>"; |
|
}; |
|
|
|
// input, select要素 |
|
var select = {}; |
|
var input = {}; |
|
|
|
["model", "subject", "nuclide", "quantity"].forEach(function(id) { |
|
select[id] = document.getElementById(id); |
|
}); |
|
|
|
[ |
|
"weight", "initial", "interval", "chronic", "start", "end", |
|
"maxTime", "physicalHalflife", "biologicalHalflife", "urineVolume" |
|
].forEach(function(id) { |
|
input[id] = document.getElementById(id); |
|
}); |
|
|
|
// 選択肢データ |
|
var models; |
|
var nuclides = [ |
|
{name: "137cs", halflife: 30.17, jp: "セシウム137"}, |
|
{name: "137cs", halflife: 2.0652, jp: "セシウム134"} |
|
]; |
|
|
|
// D3.js |
|
var w = 640, h = 500; |
|
var margin = { top: 10, left: 50, bottom: 40, right:50 }; |
|
var svg, xScale, yScale, xAxis, yAxis, xGrid, yGrid, line; |
|
var yLabel = { |
|
wholeBody: "体内残留量[Bq]", |
|
perWeight: "体重あたり残留量[Bq/kg]", |
|
urine: "1日あたり尿中排出量[Bq/日]", |
|
urineDensity: "尿中濃度[Bq/L]", |
|
dose: "1年あたり被ばく量[μSv/年]" |
|
} |
|
var timer, inTransition = false, forbidUpdate = false; |
|
|
|
// 計算用 |
|
var YEAR = 365.24; |
|
var urineRatio = 0.8; // 尿による排出の割合を80%と仮定 |
|
var stepsNumber = 100; // 時間ステップ数 |
|
var maxTime = 1000, timeStep = maxTime / stepsNumber; |
|
var data = []; // 計算データ |
|
var quantity; |
|
var K40BqPerKkg = 1.17E-4 / 6.636E-26 * 1.761E-17; // K-40 Bq / K kg |
|
|
|
d3.selectAll("input, select").on("change", update); |
|
|
|
initD3(); |
|
|
|
d3.json("models.json", function(error, json) { // モデルデータのロード |
|
|
|
models = json; |
|
|
|
models.forEach(function(m) { |
|
select["model"].add(new Option(m.label)); // モデル選択肢の追加 |
|
}); |
|
|
|
d3.select("select#model").on("change", onChangeModel); |
|
d3.select("select#subject").on("change", onChangeSubject); |
|
d3.select("select#nuclide").on("change", onChangeNuclide); |
|
|
|
onChangeModel(); |
|
|
|
}); // end of d3.json callback function |
|
|
|
function initD3() { |
|
|
|
// svg要素 |
|
svg = d3.select("svg") |
|
.attr({width: w, height: h}); |
|
|
|
// スケール |
|
xScale = d3.scale.linear() |
|
.domain([0, 1000]) |
|
.range([margin.left, w-margin.right]); |
|
yScale = d3.scale.linear() |
|
.domain([0, 1000]) |
|
.range([h-margin.bottom, margin.top]); |
|
|
|
// 軸生成関数 |
|
xAxis = d3.svg.axis() |
|
.scale(xScale) |
|
.orient("bottom"); |
|
yAxis = d3.svg.axis() |
|
.scale(yScale) |
|
.orient("left"); |
|
|
|
// グリッド生成関数 |
|
xGrid = d3.svg.axis() |
|
.scale(xScale) |
|
.orient("bottom") |
|
.tickSize(-h+margin.top+margin.bottom, 0, 0) |
|
.tickFormat(""); |
|
yGrid = d3.svg.axis() |
|
.scale(yScale) |
|
.orient("left") |
|
.tickSize(-w+margin.left+margin.right, 0, 0) |
|
.tickFormat(""); |
|
|
|
// 折れ線path要素のd属性を計算する関数 |
|
line = d3.svg.line() |
|
.x(function(d) { return xScale(d.time); }) |
|
.y(function(d) { return yScale(d.value); }); |
|
|
|
// グリッド線 |
|
svg.append("g") |
|
.attr("class", "x grid") |
|
.attr("transform", "translate(0,"+(h-margin.bottom)+")") |
|
.call(xGrid); |
|
svg.append("g") |
|
.attr("class", "y grid") |
|
.attr("transform", "translate("+ margin.left + ",0)") |
|
.call(yGrid); |
|
|
|
// x軸 |
|
svg.append("g") |
|
.attr("class", "x axis") |
|
.attr("transform", "translate(0,"+(h-margin.bottom)+")") |
|
.call(xAxis) |
|
.append("text") // ラベル |
|
.text("経過時間[日]") |
|
.attr("font-size", 10) |
|
.attr("dx", (w-margin.right)/2) |
|
.attr("dy", "3.8em"); |
|
|
|
// y軸 |
|
svg.append("g") |
|
.attr("class", "y axis") |
|
.attr("transform", "translate("+margin.left+",0)") |
|
.call(yAxis) |
|
.append("text") // ラベル |
|
.attr("class", "y label") |
|
.text("体内残留量[Bq]") |
|
.attr("font-size", 10) |
|
.attr("transform", "rotate(-90)") |
|
.attr("dy", "-3.8em") |
|
.attr("dx", -(h+margin.top-margin.bottom)/2) |
|
.attr("text-anchor", "middle"); |
|
|
|
// 折れ線 |
|
svg.append("path") |
|
.datum(data) |
|
.attr("class", "line"); |
|
|
|
} |
|
|
|
function onChangeModel() { // モデル変更時 |
|
|
|
forbidUpdate = true; |
|
|
|
document.getElementById("subjectLine").style.display = "inline"; |
|
document.getElementById("halflifeLine").style.display = "none"; |
|
|
|
// 選択されたモデルの取得 |
|
var model = models[select["model"].selectedIndex]; |
|
|
|
// 対象選択メニューの更新 |
|
while( select["subject"].options.length ) { |
|
select["subject"].remove(0); // 一旦すべての選択肢を消去 |
|
} |
|
|
|
model.subject.forEach(function(s) { |
|
select["subject"].add( new Option(s.label) ); // 選択肢を追加 |
|
}); |
|
|
|
select["subject"].selectedIndex = model.selectedIndex; // 既定の選択 |
|
|
|
// 表示量選択メニューの更新 |
|
while( select["quantity"].options.length ) { |
|
select["quantity"].remove(0); // 一旦すべての選択肢を消去 |
|
} |
|
|
|
// 共通の選択肢 |
|
select["quantity"].add( new Option( "体内残留量[Bq]", "wholeBody" ) ); |
|
select["quantity"].add( new Option( "体重あたり残留量[Bq/kg]", "perWeight") ); |
|
select["quantity"].add( new Option( "1日あたり尿中排出量[Bq/日]", "urine") ); |
|
select["quantity"].add( new Option( "尿中濃度[Bq/L]", "urineDensity") ); |
|
|
|
// ICRP publ. 56モデルのとき |
|
if( model.name == "ICRP56" ) { |
|
select["quantity"].add(new Option("1年あたり被ばく量[μSv/年]", "dose")); |
|
} |
|
|
|
// 生物学的半減期手入力モードのとき |
|
if( model.name == "halflife" ) { |
|
document.getElementById("subjectLine").style.display = "none"; // 対象選択を非表示 |
|
document.getElementById("halflifeLine").style.display = "inline"; // 半減期入力欄を表示 |
|
} |
|
|
|
onChangeSubject(); |
|
|
|
} |
|
|
|
function onChangeSubject() { // 対象変更時 |
|
|
|
forbidUpdate = true; |
|
|
|
// 選択されたモデルと対象を取得 |
|
var model = models[select["model"].selectedIndex]; |
|
var subject = model.subject[select["subject"].selectedIndex]; |
|
|
|
// 体重フォームの値を書き直す |
|
input["weight"].value = subject.weight; |
|
|
|
// 尿フォームの値を書き直す |
|
input["urineVolume"].value = subject.urine; |
|
|
|
forbidUpdate = false; |
|
|
|
update(); |
|
|
|
} |
|
|
|
function onChangeNuclide() { |
|
|
|
var nuclide = nuclides[select["nuclide"].selectedIndex]; |
|
input["physicalHalflife"].value = nuclide.halflife; |
|
|
|
update(); |
|
|
|
} |
|
|
|
function update() { |
|
|
|
clearTimeout( timer ); |
|
|
|
if ( forbidUpdate ) return; |
|
|
|
if ( inTransition ) skip(); // 進行中のグラフ変形をスキップ |
|
|
|
//////////////// |
|
// 入力データ // |
|
//////////////// |
|
|
|
// 不適当な値を強制変換 |
|
["initial", "chronic", "maxTime", "start"].forEach(function(id) { |
|
if ( Number( input[id].value ) < 0) input[id].value = 0; |
|
}); |
|
if ( Number( input["weight"].value ) <= 0 ) input["weight"].value = 1; |
|
if ( Number( input["interval"].value ) < 0.1 ) input["interval"].value = 0.1; |
|
if ( Number( input["physicalHalflife"].value ) <= 0) input["physicalHalflife"].value = 1; |
|
if ( Number( input["end"].value ) < Number( input["start"].value ) ) input["end"].value = input["start"].value; |
|
|
|
// 物理学的半減期 |
|
var Tp = Number( input["physicalHalflife"].value ) * YEAR; // 年->日 |
|
|
|
// モデル |
|
var model = models[select["model"].selectedIndex]; |
|
|
|
// 対象 |
|
var subject = model.subject[select["subject"].selectedIndex]; |
|
|
|
// 体重 |
|
var weight = Number( input["weight"].value ); |
|
|
|
// 1日の尿量 |
|
var urineVolume = Number( input["urineVolume"].value ); |
|
|
|
// モデルのパラメータ |
|
var modelParameter = subject.parameter; |
|
if (model.name == "halflife") { // 半減期モデルなら生物学的半減期を取得 |
|
if (input["biologicalHalflife"].value <= 0) { // 0以下なら1日に強制変換 |
|
input["biologicalHalflife"].value = 1; |
|
} |
|
modelParameter.T = [input["biologicalHalflife"].value]; |
|
} |
|
|
|
// 核種 |
|
var nuclide = select["nuclide"].options[select["nuclide"].selectedIndex].value; |
|
|
|
// 預託実効線量換算係数 |
|
var doseCoefficient = null; |
|
if("doseCoefficient" in subject) { |
|
doseCoefficient = Number( subject.doseCoefficient[ nuclide ] ); |
|
} |
|
|
|
// 初期摂取 |
|
var initial = Number( input["initial"].value ); |
|
// 慢性摂取 |
|
var interval = Number( input["interval"].value ); // 摂取間隔 |
|
var chronic = Number( input["chronic"].value ); // 1回あたり摂取量 |
|
var start = Number( input["start"].value ); // 摂取開始時刻 |
|
var end = Number( input["end"].value ); // 摂取終了時刻 |
|
|
|
// 最大表示時間 |
|
maxTime = Number( input["maxTime"].value ); |
|
|
|
// 計算する量 |
|
quantity = select["quantity"].options[select["quantity"].selectedIndex].value; |
|
|
|
////////// |
|
// 計算 // |
|
////////// |
|
|
|
// 経時変化計算開始 // |
|
|
|
data = []; |
|
timeStep = maxTime/stepsNumber; |
|
|
|
if ( timeStep >= interval ) { // (1) 摂取間隔<=時間ステップのとき: 連続摂取近似 |
|
|
|
for( var i = 0; timeStep * i <= maxTime; i ++ ) { |
|
|
|
var t = Math.min( timeStep * i, maxTime ); // 時刻 |
|
|
|
switch( quantity ) { |
|
case "wholeBody": // 体内残留量[Bq] |
|
var v = initial * irf(t); |
|
v += chronic / interval * irfContinuous( t, start, end ); |
|
data.push( { time: t, value: v } ); |
|
break; |
|
case "perWeight": // 体重あたり残留量[Bq/kg] |
|
var v = initial * irf(t); |
|
v += chronic / interval * irfContinuous( t, start, end ); |
|
v /= weight; |
|
data.push( { time: t, value: v } ); |
|
break; |
|
case "dose": // 1年あたり被ばく量[uSv/年] |
|
var v = initial * irf(t); |
|
v += chronic / interval * irfContinuous( t, start, end ); |
|
v = v * doseCoefficient * 1e+6 / irfArea() * YEAR; |
|
data.push( { time: t, value: v } ); |
|
break; |
|
case "urine": // 1日あたり尿中排出量[Bq/日] |
|
var v = initial * ief(t); |
|
v += chronic / interval * iefContinuous( t, start, end ); |
|
v *= urineRatio; |
|
data.push( { time: t, value: v } ); |
|
break; |
|
case "urineDensity": // 尿中濃度[Bq/L] |
|
var v = initial * ief(t); |
|
v += chronic / interval * iefContinuous( t, start, end ); |
|
v *= urineRatio / urineVolume; |
|
data.push( { time: t, value: v } ); |
|
break; |
|
} |
|
|
|
} |
|
|
|
} else if ( timeStep < interval ) { // (2) 摂取間隔>時間ステップのとき: 逐次計算 |
|
|
|
intakeTime = []; // 摂取時刻 |
|
for ( var i = 0; i * interval <= end - start; i ++ ) { |
|
intakeTime.push( start + i * interval ); |
|
} |
|
|
|
// 初期値 |
|
var t = 0; |
|
var compartments = []; |
|
modelParameter.forEach(function(p) { |
|
compartments.push( p.a * initial ); // 初期急性摂取 |
|
}); |
|
addData(); |
|
|
|
for ( var i = 0; i < intakeTime.length; i++ ) { |
|
|
|
var nextIntakeTime = intakeTime[i]; |
|
|
|
if ( nextIntakeTime >= maxTime ) break; |
|
|
|
// 次の摂取の前まで |
|
while ( t < nextIntakeTime - timeStep ) { |
|
t += timeStep; |
|
for ( var j = 0; j < compartments.length; j++ ) { |
|
compartments[j] *= Math.pow( 0.5, timeStep / modelParameter[j].T ); |
|
} |
|
addData(); |
|
} |
|
|
|
// 次の摂取直前 |
|
for ( var j = 0; j < compartments.length; j++ ) { |
|
compartments[j] *= Math.pow( 0.5, (nextIntakeTime - t) / modelParameter[j].T ); |
|
} |
|
t = nextIntakeTime; |
|
addData(); |
|
|
|
// 摂取直後 |
|
for ( var j = 0; j < compartments.length; j++ ) { |
|
compartments[j] += chronic * modelParameter[j].a; |
|
} |
|
addData(); |
|
|
|
} |
|
|
|
// 摂取終了後から最大表示時刻手前まで |
|
while ( t <= maxTime - timeStep ) { |
|
t += timeStep; |
|
for ( var j = 0; j < compartments.length; j++ ) { |
|
compartments[j] *= Math.pow( 0.5, timeStep / modelParameter[j].T ); |
|
} |
|
addData(); |
|
} |
|
|
|
// 最大表示時刻まで |
|
for ( var j = 0; j < compartments.length; j++ ) { |
|
compartments[j] *= Math.pow( 0.5, (maxTime - t) / modelParameter[j].T ); |
|
} |
|
t = maxTime; |
|
addData(); |
|
|
|
function addData() { |
|
|
|
switch( quantity ) { |
|
case "wholeBody": // 体内残留量[Bq] |
|
var v = d3.sum( compartments ); |
|
data.push( { time: t, value: v } ); |
|
break; |
|
case "perWeight": // 体重あたり残留量[Bq/kg] |
|
var v = d3.sum( compartments ); |
|
v /= weight; |
|
data.push( { time: t, value: v } ); |
|
break; |
|
case "dose": // 1年あたり被ばく量[uSv/年] |
|
var v = d3.sum( compartments ); |
|
v *= doseCoefficient * 1e+6 / irfArea() * YEAR; |
|
data.push( { time: t, value: v } ); |
|
break; |
|
case "urine": // 1日あたり尿中排出量[Bq/日] |
|
var v = 0; |
|
for( var j = 0; j < compartments.length; j++ ) { |
|
v += compartments[j] / modelParameter[j].T |
|
} |
|
v *= Math.LN2 * urineRatio; |
|
data.push( { time: t, value: v } ); |
|
break; |
|
case "urineDensity": // 尿中濃度[Bq/L] |
|
var v = 0; |
|
for( var j = 0; j < compartments.length; j++ ) { |
|
v += compartments[j] / modelParameter[j].T |
|
} |
|
v *= Math.LN2 * urineRatio / urineVolume; |
|
data.push( { time: t, value: v } ); |
|
break; |
|
} |
|
|
|
} // end of addData() // |
|
|
|
} // 経時変化計算終了 // |
|
|
|
// 総計 |
|
var totalHTML = "<strong>総計</strong>"; |
|
var total = {intake: 0, dose: 0}; |
|
|
|
total.intake = initial + chronic * (end - start) / interval; |
|
totalHTML += "<br> 総摂取量: <strong>" + total.intake.toScientificNotation(3) + " Bq</strong>"; |
|
|
|
if(doseCoefficient) { // 線量係数があるとき |
|
total.dose = total.intake * doseCoefficient * 1e+6; |
|
totalHTML += "<br> 預託実効線量: <strong>" + total.dose.toScientificNotation(2) + " μSv</strong>"; |
|
} |
|
|
|
document.getElementById("total").innerHTML = totalHTML; |
|
|
|
// 定常状態での量 |
|
var steadyHTML = "<strong>定常状態では</strong>"; |
|
var steady = { retention: 0, perWeight: 0, dose: 0, urineDensity: 0 }; |
|
|
|
modelParameter.forEach(function(p) { |
|
steady.retention += p.a / ( 1/Tp + 1/p.T ); |
|
}); |
|
steady.retention = chronic / interval / Math.LN2 * steady.retention; |
|
steadyHTML += "<br> 体内残留量: <strong>" + steady.retention.toScientificNotation(3) + " Bq</strong>"; |
|
|
|
steady.perWeight = steady.retention / weight; |
|
steadyHTML += "<br> 体重あたり残留量: <strong>" + steady.perWeight.toScientificNotation(3) + " Bq/kg</strong>"; |
|
|
|
if (doseCoefficient) { |
|
steady.dose = steady.retention / irfArea() * doseCoefficient * 1e+6 * YEAR; |
|
steadyHTML += "<br> 1年あたり被ばく量: <strong>" + steady.dose.toScientificNotation(2) + " μSv/年</strong>"; |
|
} |
|
|
|
steady.urineDensity = chronic * urineRatio / urineVolume; |
|
steadyHTML += "<br> 尿中濃度: <strong>" + steady.urineDensity.toScientificNotation(3) + " Bq/L</strong>"; |
|
|
|
document.getElementById("steady").innerHTML = steadyHTML; |
|
|
|
// K-40 |
|
var k40 = weight * K40BqPerKkg; |
|
k40 *= subject.label == "3ヶ月" ? 0.00285 : 0.00202; // K composition (wt) |
|
document.getElementById("k40").innerHTML = k40.toScientificNotation(3); |
|
|
|
// グラフ変形開始 |
|
startTransition(); |
|
|
|
////////// |
|
// 関数 // |
|
////////// |
|
|
|
// IRF(Intake Retention Fraction): t=0に単位量急性摂取のときの解, つまりグリーン関数のこと |
|
function irf(t) { |
|
|
|
if ( t >= 0 ) { |
|
|
|
var compartments = []; |
|
modelParameter.forEach(function(p) { |
|
compartments.push( p.a * Math.pow(0.5, t/p.T) ); |
|
}); |
|
|
|
return d3.sum( compartments )*Math.pow(0.5, t/Tp); |
|
|
|
} else { |
|
|
|
return 0; |
|
|
|
} |
|
|
|
} |
|
|
|
// IRFの積分[日] |
|
function irfArea() { |
|
var area = 0; |
|
modelParameter.forEach(function(p) { |
|
area += p.a * p.T; |
|
}); |
|
area /= Math.LN2; |
|
return area; |
|
} |
|
|
|
// t=tStartからt=tEndまで1単位/日連続摂取時の解 |
|
function irfContinuous(t, tStart, tEnd) { |
|
|
|
if ( typeof tStart === "undefined" ) tStart = 0; |
|
if ( typeof tEnd === "undefined" ) tEnd = maxTime; |
|
|
|
if ( t < tStart ) { |
|
|
|
return 0; |
|
|
|
} else if ( t >= tStart ) { |
|
|
|
var compartments = []; |
|
var t0 = t-tStart; |
|
var t1 = t-tEnd; |
|
|
|
if ( t1 > 0 ) { |
|
|
|
modelParameter.forEach(function(p) { |
|
var Te = 1/(1/p.T + 1/Tp); |
|
compartments.push( |
|
p.a * Te * ( Math.pow(0.5, t1/Te) - Math.pow(0.5, t0/Te) ) |
|
); |
|
}); |
|
|
|
} else if ( t1 <= 0 ) { |
|
|
|
modelParameter.forEach(function(p) { |
|
var Te = 1/(1/p.T + 1/Tp); |
|
compartments.push( |
|
p.a * Te * ( 1 - Math.pow(0.5, t0/Te) ) |
|
); |
|
}); |
|
|
|
} |
|
|
|
return d3.sum( compartments ) / Math.LN2; |
|
|
|
} else { |
|
|
|
console.log( "error irfContinuous" ); |
|
|
|
} |
|
|
|
} |
|
|
|
// IEF(Intake Excretion Fraction): t=0に単位量急性摂取のときの排出率[1/日] |
|
function ief(t) { |
|
|
|
if ( t >= 0 ) { |
|
|
|
var compartments = []; |
|
modelParameter.forEach(function(p) { |
|
compartments.push( p.a / p.T * Math.pow(0.5, t/p.T) ); |
|
}); |
|
|
|
return d3.sum( compartments ) * Math.LN2 * Math.pow(0.5, t/Tp); |
|
|
|
} else { |
|
|
|
return 0; |
|
|
|
} |
|
|
|
} |
|
|
|
// t=tStartからt=tEndまで1単位/日連続摂取時の排出速度 |
|
function iefContinuous(t, tStart, tEnd) { |
|
|
|
if ( typeof tStart === "undefined" ) tStart = 0; |
|
if ( typeof tEnd === "undefined" ) tEnd = maxTime; |
|
|
|
if ( t < tStart ) { |
|
|
|
return 0; |
|
|
|
} else if ( t >= tStart ) { |
|
|
|
var compartments = []; |
|
var t0 = t-tStart; |
|
var t1 = t-tEnd; |
|
|
|
if ( t1 > 0 ) { |
|
|
|
modelParameter.forEach(function(p) { |
|
var Te = 1/(1/p.T + 1/Tp); |
|
compartments.push( |
|
p.a * Te / p.T * ( Math.pow(0.5, t1/Te) - Math.pow(0.5, t0/Te) ) |
|
); |
|
}); |
|
|
|
} else if ( t1 <= 0 ) { |
|
|
|
modelParameter.forEach(function(p) { |
|
var Te = 1/(1/p.T + 1/Tp); |
|
compartments.push( |
|
p.a * Te / p.T * ( 1 - Math.pow(0.5, t0/Te) ) |
|
); |
|
}); |
|
|
|
} |
|
|
|
return d3.sum( compartments ); |
|
|
|
} else { |
|
|
|
console.log( "error iefContinuous" ); |
|
|
|
} |
|
|
|
} |
|
|
|
} // end of update() |
|
|
|
// グラフ変形関数 |
|
function startTransition() { |
|
|
|
inTransition = true; |
|
timer = setTimeout(function() { |
|
inTransition = false; |
|
}, 2000); |
|
|
|
// 2段階のアニメーションで変形する |
|
// 1段階目: 最初の1秒間 |
|
// 折れ線を描き直す |
|
svg.select(".line") |
|
.datum(data) |
|
.transition() |
|
.duration(1000) |
|
.attr("d", line); |
|
|
|
// 2段階目: 1秒待って次の1秒間 |
|
// y軸のデータ範囲を取り直す |
|
yScale.domain([0, d3.max(data, function(d) { return d.value; } )]); |
|
xScale.domain([0, maxTime]); |
|
// 軸を描き直す |
|
svg.select(".x.axis") |
|
.transition() |
|
.delay(1000) |
|
.duration(1000) |
|
.call(xAxis); |
|
svg.select(".y.axis") |
|
.transition() |
|
.delay(1000) |
|
.duration(1000) |
|
.call(yAxis); |
|
svg.select(".y.label") |
|
.transition() |
|
.delay(1000) |
|
.duration(1000) |
|
.text(yLabel[quantity]); |
|
|
|
// グリッドを描き直す |
|
svg.select(".x.grid") |
|
.transition() |
|
.delay(1000) |
|
.duration(1000) |
|
.call(xGrid); |
|
svg.select(".y.grid") |
|
.transition() |
|
.delay(1000) |
|
.duration(1000) |
|
.call(yGrid); |
|
// 軸に合わせて折れ線を描き直す |
|
svg.select(".line") |
|
.transition() |
|
.delay(1000) |
|
.duration(1000) |
|
.attr("d", line); |
|
|
|
} // end of transition() |
|
|
|
// グラフ変形をスキップ |
|
function skip() { |
|
|
|
svg.select(".line") |
|
.interrupt() |
|
.attr("d", line); |
|
|
|
yScale.domain([0, d3.max(data, function(d) { return d.value; } )]); |
|
xScale.domain([0, maxTime]); |
|
|
|
svg.select(".x.axis") |
|
.interrupt() |
|
.transition() |
|
.duration(0) |
|
.call(xAxis); |
|
|
|
svg.select(".y.axis") |
|
.interrupt() |
|
.transition() |
|
.duration(0) |
|
.call(yAxis); |
|
|
|
svg.select(".y.label") |
|
.interrupt() |
|
.transition() |
|
.duration(0) |
|
.text(yLabel[quantity]); |
|
|
|
svg.select(".x.grid") |
|
.interrupt() |
|
.transition() |
|
.duration(0) |
|
.call(xGrid); |
|
svg.select(".y.grid") |
|
.interrupt() |
|
.transition() |
|
.duration(0) |
|
.call(yGrid); |
|
|
|
inTransition = false; |
|
|
|
} // end of skip() |
|
|
|
})(); |
|
</script> |
|
</body> |
|
</html> |