<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Tools on SPECie | SPECjalna strona o SPECjalnych rzeczach</title><link>https://specie.pl/tools/</link><description>Recent content in Tools on SPECie | SPECjalna strona o SPECjalnych rzeczach</description><generator>Hugo</generator><language>pl</language><managingEditor>pawel.piatek@specie.pl (Paweł)</managingEditor><webMaster>pawel.piatek@specie.pl (Paweł)</webMaster><lastBuildDate>Tue, 05 May 2026 21:33:51 +0200</lastBuildDate><atom:link href="https://specie.pl/tools/index.xml" rel="self" type="application/rss+xml"/><item><title>Symulator dynamiki przepływu</title><link>https://specie.pl/tools/dcsim/</link><pubDate>Tue, 05 May 2026 21:33:51 +0200</pubDate><author>pawel.piatek@specie.pl (Paweł)</author><guid>https://specie.pl/tools/dcsim/</guid><description>&lt;p&gt;W poniższym symulatorze możesz sprawdzić dynamikę przepływu przy kilku zmiennych. Nie działa na smartfonie? spróbuj na komputerze.&lt;/p&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/chart.js"&gt;&lt;/script&gt;
&lt;style&gt;
 
 :root {
 --gb-bg: #282828;
 --gb-bg-panel: #3c3836;
 --gb-bg-hover: #504945;
 --gb-fg: #ebdbb2;
 --gb-fg-dark: #a89984;
 --gb-orange: #fe8019;
 --gb-yellow: #fabd2f;
 --gb-border: #665c54;
 }

 .gruvbox-simulator {
 background-color: var(--gb-bg);
 color: var(--gb-fg);
 border-radius: 8px;
 box-shadow: 0 8px 32px rgba(0,0,0,0.3);
 padding: 25px;
 width: 100%;
 max-width: 100%;
 display: grid;
 
 grid-template-columns: minmax(0, 1.3fr) minmax(250px, 1fr);
 gap: 25px;
 margin: 2rem 0;
 box-sizing: border-box;
 font-family: 'Courier New', Courier, monospace;
 border: 1px solid var(--gb-border);
 overflow: hidden;
 }

 
 @media (max-width: 850px) {
 .gruvbox-simulator {
 grid-template-columns: 1fr;
 }
 }

 .chart-wrapper {
 position: relative;
 height: 450px;
 width: 100%;
 background: var(--gb-bg-panel);
 border-radius: 6px;
 padding: 15px;
 border: 1px solid var(--gb-border);
 box-sizing: border-box;
 }

 .controls-wrapper {
 display: flex;
 flex-direction: column;
 gap: 15px;
 width: 100%;
 box-sizing: border-box;
 }

 .controls-wrapper h3 {
 margin: 0 0 10px 0;
 font-size: 1.2rem;
 color: var(--gb-yellow);
 border-bottom: 2px dashed var(--gb-border);
 padding-bottom: 10px;
 text-transform: uppercase;
 letter-spacing: 1px;
 }

 .ctrl-group {
 display: flex;
 flex-direction: column;
 background: var(--gb-bg-panel);
 padding: 12px 15px;
 border-radius: 6px;
 border: 1px solid transparent;
 transition: border-color 0.2s;
 }

 .ctrl-group:hover {
 border-color: var(--gb-orange);
 }

 .ctrl-group label {
 font-weight: bold;
 margin-bottom: 10px;
 font-size: 0.85rem;
 display: flex;
 justify-content: space-between;
 align-items: center;
 color: var(--gb-fg-dark);
 text-transform: uppercase;
 }

 .val-display {
 color: var(--gb-orange);
 font-size: 1.05rem;
 font-weight: bold;
 text-transform: none; 
 }

 input[type=range] {
 -webkit-appearance: none;
 width: 100%;
 background: transparent;
 }
 input[type=range]:focus {
 outline: none;
 }
 input[type=range]::-webkit-slider-thumb {
 -webkit-appearance: none;
 height: 16px;
 width: 16px;
 border-radius: 2px;
 background: var(--gb-orange);
 cursor: pointer;
 margin-top: -6px;
 }
 input[type=range]::-webkit-slider-runnable-track {
 width: 100%;
 height: 4px;
 cursor: pointer;
 background: var(--gb-bg-hover);
 border-radius: 2px;
 }

 .stats-box {
 margin-top: auto;
 padding: 15px;
 background: var(--gb-bg-hover);
 border-radius: 6px;
 font-size: 0.85rem;
 line-height: 1.5;
 color: var(--gb-fg);
 }
 .stats-box span {
 color: var(--gb-yellow);
 font-weight: bold;
 }
&lt;/style&gt;

&lt;div class="gruvbox-simulator"&gt;
 &lt;div class="chart-wrapper"&gt;
 &lt;canvas id="flowChart"&gt;&lt;/canvas&gt;
 &lt;/div&gt;

 &lt;div class="controls-wrapper"&gt;
 &lt;h3&gt;Parametry układu&lt;/h3&gt;

 &lt;div class="ctrl-group"&gt;
 &lt;label&gt;Masa kawy &lt;span class="val-display" id="valDose"&gt;15 g&lt;/span&gt;&lt;/label&gt;
 &lt;input type="range" id="simDose" min="10" max="35" step="0.5" value="15"&gt;
 &lt;/div&gt;

 &lt;div class="ctrl-group"&gt;
 &lt;label&gt;Masa wody &lt;span class="val-display" id="valWater"&gt;250 g&lt;/span&gt;&lt;/label&gt;
 &lt;input type="range" id="simWater" min="150" max="500" step="10" value="250"&gt;
 &lt;/div&gt;

 &lt;div class="ctrl-group"&gt;
 &lt;label&gt;Zmielenie ziarna &lt;span class="val-display" id="valGrind"&gt;600 µm&lt;/span&gt;&lt;/label&gt;
 &lt;input type="range" id="simGrind" min="300" max="1100" step="20" value="600"&gt;
 &lt;/div&gt;

 &lt;div class="ctrl-group"&gt;
 &lt;label&gt;Temperatura wody &lt;span class="val-display" id="valTemp"&gt;93 °C&lt;/span&gt;&lt;/label&gt;
 &lt;input type="range" id="simTemp" min="70" max="100" step="1" value="93"&gt;
 &lt;/div&gt;

 &lt;div class="ctrl-group"&gt;
 &lt;label&gt;Udział pyłu (Fines) &lt;span class="val-display" id="valFines"&gt;8 %&lt;/span&gt;&lt;/label&gt;
 &lt;input type="range" id="simFines" min="0" max="25" step="1" value="8"&gt;
 &lt;/div&gt;

 &lt;div class="stats-box"&gt;
 Ratio ekstrakcji: &lt;span id="statRatio"&gt;1:16.7&lt;/span&gt;&lt;br&gt;
 Szacowany czas: &lt;span id="statTime"&gt;0:00&lt;/span&gt;
 &lt;/div&gt;
 &lt;/div&gt;
&lt;/div&gt;

&lt;script&gt;
 Chart.defaults.color = '#a89984';
 Chart.defaults.font.family = "'Courier New', Courier, monospace";

 const chartCtx = document.getElementById('flowChart').getContext('2d');
 let dynamicChart = null;

 const domInputs = {
 dose: document.getElementById('simDose'),
 water: document.getElementById('simWater'),
 grind: document.getElementById('simGrind'),
 temp: document.getElementById('simTemp'),
 fines: document.getElementById('simFines')
 };

 const domDisplays = {
 dose: document.getElementById('valDose'),
 water: document.getElementById('valWater'),
 grind: document.getElementById('valGrind'),
 temp: document.getElementById('valTemp'),
 fines: document.getElementById('valFines'),
 ratio: document.getElementById('statRatio'),
 time: document.getElementById('statTime')
 };

 function runSimulation(dose, water, grind, temp, fines) {
 let plotData = [];
 let plotLabels = [];
 let currentVol = 0;
 let t = 0;
 const timeStep = 1;
 const maxTime = 420;

 let viscosityModifier = 1 + (93 - temp) * 0.015;

 while (t &lt;= maxTime) {
 plotLabels.push(t);

 if (currentVol &gt;= water) {
 plotData.push(water);
 t += timeStep;
 continue;
 }

 let baseFlow = Math.pow(grind / 600, 2) * (15 / dose) * 2.8;
 baseFlow = baseFlow / viscosityModifier;

 let migrationState = Math.pow(currentVol / water, 1.8);
 let clogging = 1 + (fines / 10) * migrationState * 2;

 let flowRate = baseFlow / clogging;
 currentVol += flowRate * timeStep;

 if (currentVol &gt; water) currentVol = water;
 plotData.push(currentVol);

 t += timeStep;
 }

 let finishTime = plotData.findIndex(v =&gt; v &gt;= water);
 if(finishTime === -1) finishTime = maxTime;

 return { labels: plotLabels, data: plotData, target: water, finishTime };
 }

 function formatTime(seconds) {
 if(seconds &gt;= 420) return "&gt; 7:00 (Zatkano)";
 let min = Math.floor(seconds / 60);
 let sec = seconds % 60;
 return `${min}:${sec &lt; 10 ? '0' : ''}${sec}`;
 }

 function renderChart() {
 const vDose = parseFloat(domInputs.dose.value);
 const vWater = parseFloat(domInputs.water.value);
 const vGrind = parseFloat(domInputs.grind.value);
 const vTemp = parseFloat(domInputs.temp.value);
 const vFines = parseFloat(domInputs.fines.value);

 domDisplays.dose.textContent = vDose + ' g';
 domDisplays.water.textContent = vWater + ' g';
 domDisplays.grind.textContent = vGrind + ' µm'; 
 domDisplays.temp.textContent = vTemp + ' °C';
 domDisplays.fines.textContent = vFines + ' %';

 let ratio = (vWater / vDose).toFixed(1);
 domDisplays.ratio.textContent = `1:${ratio}`;

 const sim = runSimulation(vDose, vWater, vGrind, vTemp, vFines);
 domDisplays.time.textContent = formatTime(sim.finishTime);

 if (dynamicChart) {
 dynamicChart.data.labels = sim.labels;
 dynamicChart.data.datasets[0].data = sim.data;
 dynamicChart.options.scales.y.max = Math.ceil(vWater / 50) * 50 + 20;
 dynamicChart.update();
 } else {
 dynamicChart = new Chart(chartCtx, {
 type: 'line',
 data: {
 labels: sim.labels,
 datasets: [{
 label: 'Przepływ naparu (g)',
 data: sim.data,
 borderColor: '#fe8019',
 backgroundColor: 'rgba(254, 128, 25, 0.15)',
 borderWidth: 2,
 fill: true,
 tension: 0.3,
 pointRadius: 0
 }]
 },
 options: {
 responsive: true,
 maintainAspectRatio: false,
 animation: { duration: 150 },
 scales: {
 x: {
 grid: { color: '#504945' },
 title: { display: true, text: 'Czas (s)', color: '#fabd2f' },
 ticks: { maxTicksLimit: 10 }
 },
 y: {
 min: 0,
 grid: { color: '#504945' },
 title: { display: true, text: 'Masa (g)', color: '#fabd2f' }
 }
 },
 plugins: {
 legend: { display: false },
 tooltip: {
 backgroundColor: '#282828',
 titleColor: '#fabd2f',
 bodyColor: '#ebdbb2',
 borderColor: '#665c54',
 borderWidth: 1,
 callbacks: {
 label: (ctx) =&gt; `${ctx.parsed.y.toFixed(1)} g`
 }
 }
 }
 }
 });
 }
 }

 Object.values(domInputs).forEach(input =&gt; {
 input.addEventListener('input', renderChart);
 });

 renderChart();
&lt;/script&gt;

&lt;h1 id="uwaga-to-silnie-uproszczony-model-lepiej-traktować-go-jako-ciekawostkę-i-zabawkę-a-nie-jako-wyznacznik-czy-wyrocznie"&gt;Uwaga! To silnie uproszczony model, lepiej traktować go jako ciekawostkę i zabawkę, a nie jako wyznacznik czy wyrocznie.&lt;a href="#uwaga-to-silnie-uproszczony-model-lepiej-traktowa%c4%87-go-jako-ciekawostk%c4%99-i-zabawk%c4%99-a-nie-jako-wyznacznik-czy-wyrocznie" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;h3 id="1-przepływ-bazowy-zmodyfikowane-prawo-darcyego"&gt;1. Przepływ Bazowy (Zmodyfikowane Prawo Darcy&amp;rsquo;ego)&lt;a href="#1-przep%c5%82yw-bazowy-zmodyfikowane-prawo-darcyego" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Równanie to opisuje teoretyczne natężenie przepływu wody przez łóżko kawy, traktowane jako ośrodek porowaty, bez uwzględnienia oporów dodatkowych generowanych przez pył czy temperaturę.&lt;/p&gt;</description></item><item><title>Kalkulator parametrów wody</title><link>https://specie.pl/tools/water_calc/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><author>pawel.piatek@specie.pl (Paweł)</author><guid>https://specie.pl/tools/water_calc/</guid><description>&lt;p&gt;Poniżej znajduje się wstępna wersja mojego narzędzia do obliczania parametrów wody do parzenia kawy. SCA (Speciality Coffe Association) określa optymalny skład wody używanej do parzenia kawy:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TDS: 70-250 ppm&lt;/li&gt;
&lt;li&gt;GH: 50-175 ppm&lt;/li&gt;
&lt;li&gt;KH: 40-70 ppm&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Te wartości nie są regułą! Kawa na innej wodzie może być dużo lepsza niż taka na tej, więc nie przestawaj szukać swojego idealnego sposobu.&lt;/p&gt;
&lt;style&gt;
 
 .gruvbox-calc {
 background-color: #282828;
 color: #ebdbb2;
 font-family: 'Courier New', Courier, monospace;
 padding: 25px;
 border-radius: 8px;
 max-width: 650px;
 margin: 20px auto;
 box-shadow: 0 4px 15px rgba(0,0,0,0.3);
 border: 2px solid #504945;
 }
 .gruvbox-calc h3, .gruvbox-calc h4 {
 color: #fabd2f; 
 margin-top: 0;
 border-bottom: 1px dashed #504945;
 padding-bottom: 10px;
 }
 .gruvbox-calc label {
 display: block;
 margin-top: 15px;
 margin-bottom: 5px;
 color: #83a598; 
 font-weight: bold;
 }
 .gruvbox-calc input[type="number"] {
 width: 100%;
 padding: 10px;
 background-color: #3c3836;
 color: #ebdbb2;
 border: 1px solid #665c54;
 border-radius: 4px;
 box-sizing: border-box;
 font-family: inherit;
 }
 .gruvbox-calc input[type="number"]:focus {
 outline: none;
 border-color: #fe8019; 
 }
 .gruvbox-calc .section {
 background-color: #32302f;
 padding: 15px;
 border-radius: 6px;
 margin-bottom: 20px;
 border-left: 4px solid #fe8019;
 }
 .gruvbox-calc .results {
 background-color: #3c3836;
 padding: 20px;
 border-radius: 6px;
 border: 1px solid #8ec07c; 
 }
 .gruvbox-calc table {
 width: 100%;
 border-collapse: collapse;
 margin-top: 15px;
 font-size: 0.9em;
 }
 .gruvbox-calc th, .gruvbox-calc td {
 border: 1px solid #504945;
 padding: 8px;
 text-align: left;
 }
 .gruvbox-calc th {
 background-color: #504945;
 color: #b8bb26; 
 }
 .gruvbox-calc .highlight {
 color: #fe8019;
 font-weight: bold;
 font-size: 1.1em;
 }
&lt;/style&gt;

&lt;div class="gruvbox-calc"&gt;
 &lt;h3&gt;Kalkulator Koncentratów &lt;/h3&gt;

 &lt;div class="section"&gt;
 &lt;h4&gt;Parametry koncentratów&lt;/h4&gt;
 &lt;p style="font-size: 0.85em; color: #a89984;"&gt;Przygotowujemy dwa osobne koncentraty. Ich moc jest zoptymalizowana tak, aby &lt;strong&gt;1 ml roztworu podnosił parametr o 10 ppm w 1 litrze wody&lt;/strong&gt;.&lt;/p&gt;</description></item></channel></rss>