+
+
- - ⬅
- - ⬉
- - ⬆
- - ⬈
- - ?
+ - +
+ - -
+ - ?
+
¡SIMULAR!
©2024 Centro de Simulación Computacional para Aplicaciones Tecnológicas (CSC) - CONICET

Rotá tu
dispositivo
diff --git a/js/main.js b/js/main.js
index b7e3acb..08c39ee 100644
--- a/js/main.js
+++ b/js/main.js
@@ -26,8 +26,11 @@ class WindSimulation {
this.wm = null;
this.simulation = null;
+ this.simulationPot = null;
this.xhr = null;
+ this.lastFrame = Date.now();
+
this.rotation = Math.PI;
this.targetRotation = Math.PI;
this.dir = 0;
@@ -64,18 +67,19 @@ class WindSimulation {
for(var i = 0; i < this.mills; i++) {
var wm = new WindMill();
wm.position.set(4 + 0.5, i + 5 - Math.ceil(this.mills / 2) + 0.5, 0);
- wm.rotation.z = Math.PI;
+ wm.rotation.z = this.targetRotation;
scene.add(wm);
this.wms.push(wm);
}
this.wm = this.wms[0];
}
- move(pos, ended=false) {
- if(this.simulation) {
- scene.remove(this.simulation);
- this.simulation = null;
- }
+ move(pos, ended=false, count=2) {
+ if(this.simulation)
+ this.resetSimulation();
+
+ if(! count)
+ return false;
var wm = this.wm;
@@ -85,11 +89,22 @@ class WindSimulation {
}
if(wm == null) return false;
- if(pos.x > this.width - 0.5 || pos.x < 0.5 || pos.y > this.height - 0.5 || pos.y < 0.5) return false;
+ if(pos.x > this.width - 0.5 || pos.x < 0.5 || pos.y > this.height - 0.5 || pos.y < 0.5) {
+ if(pos.x > this.width - 0.5)
+ pos.x = this.width - 0.5;
+ if(pos.x < 0.5)
+ pos.x = 0.5;
+ if(pos.y > this.height - 0.5)
+ pos.y = this.height - 0.5;
+ if(pos.y < 0.5)
+ pos.y = 0.5;
+ return this.move(pos, ended, count - 1);
+ }
for(var i = 0; i < this.wms.length; i++) {
if(this.wms[i] == wm) continue;
- if(pos.clone().sub(this.wms[i].position).length() < 1)
- return false;
+ if(pos.clone().sub(this.wms[i].position).length() < 1) {
+ return this.move(this.wms[i].position.clone().add(pos.clone().sub(this.wms[i].position).normalize()), ended, count - 1);
+ }
}
wm.position.x = pos.x;
wm.position.y = pos.y;
@@ -99,11 +114,14 @@ class WindSimulation {
animate() {
requestAnimationFrame(() => { this.animate(); });
+ var now = Date.now();
+ var step = (now - this.lastFrame) / 1000;
+
if(!Screen.isMobile || (Screen.isMobile && screen.clicked)) {
const intersects = screen.raycaster.intersectObjects(this.wms);
var wm = null;
for(var i = 0; i < intersects.length; i++) {
- if(intersects[i].object.geometry.constructor.name != "CylinderGeometry") {
+ if(intersects[i].object.constructor.name != "Spring") {
wm = intersects[i].object.parent;
break;
}
@@ -111,22 +129,32 @@ class WindSimulation {
if(!screen.clicked || Screen.isMobile) {
if(this.wm != wm) {
- if(this.wm != null) this.wm.select(false);
- if(wm != null) wm.select(true);
+ if(this.wm != null) {
+ this.wm.select(false);
+ document.getElementById("follower").style.display = "none";
+ }
+ if(wm != null) {
+ wm.select(true);
+ if(this.simulation) {
+ var i = this.wms.indexOf(wm);
+ var follower = document.getElementById("follower");
+ follower.innerHTML = "Aerogenerador Nº" + (i + 1) + "
" + (Math.round(this.simulationPot[i] * 100) / 100) + " GWh (" + Math.round(this.simulationPot[i] * 100 / 14.95) + "%)";
+ follower.style.display = "block";
+ }
+ }
}
this.wm = wm;
}
}
for(var i = 0; i < this.wms.length; i++)
- this.wms[i].animate();
+ this.wms[i].animate(step);
- var speed = 0.02;
+ var speed = 2 * step;
if(Math.abs(this.targetRotation - this.rotation) > speed) {
var step = speed * Math.sign(this.targetRotation - this.rotation);
this.rotation += step;
for(var i = 0; i < this.wms.length; i++) {
- this.wms[i].animate();
this.wms[i].rotation.z += step;
}
this.flag.rotation.z += step;
@@ -134,14 +162,25 @@ class WindSimulation {
this.flag.animate();
+ if(window.exclamation)
+ window.exclamation.rotation.y += 0.02;
+
+ this.lastFrame = now;
+
renderer.render(scene, camera);
}
+ resetSimulation() {
+ scene.remove(this.simulation);
+ this.simulation = null;
+ this.simulationPot = null;
+ document.getElementById("follower").style.display = "none";
+ document.getElementById("results").style.display = "none";
+ }
+
setRotation(dir) {
- if(this.simulation) {
- scene.remove(this.simulation);
- this.simulation = null;
- }
+ if(this.simulation)
+ this.resetSimulation();
var rotations = {};
rotations[0] = Math.PI;
@@ -153,6 +192,30 @@ class WindSimulation {
this.targetRotation = rotations[dir];
}
+ addWindMill() {
+ if(this.mills >= 10) return;
+
+ this.mills++;
+ this.reset();
+ }
+
+ removeWindMill() {
+ if(this.mills <= 1) return;
+
+ this.mills--;
+ this.reset();
+ }
+
+ reset() {
+ for(var i = 0; i < this.wms.length; i++)
+ scene.remove(this.wms[i]);
+
+ this.wms = [];
+ this.rotation = this.targetRotation;
+
+ this.init_windmills();
+ }
+
simulate() {
if(this.xhr != null)
this.xhr.abort();
@@ -165,7 +228,8 @@ class WindSimulation {
if(this.xhr != xhr) return;
if (xhr.readyState === 4 && xhr.status === 200) {
var json = JSON.parse(xhr.responseText);
- this.add_simulation(json.ws);
+ console.log(json.pot)
+ this.add_simulation(json.ws, json.pot);
this.xhr = null;
}
};
@@ -176,7 +240,17 @@ class WindSimulation {
xhr.send(data);
}
- add_simulation(ws) {
+ add_simulation(ws, pot) {
+ var sum = 0;
+ for(let i = 0; i < pot.length; i++) {
+ this.wms[i].lowPower(pot[i] < 2.75/*14.95 * 0.8*/)
+ sum += pot[i];
+ }
+
+ var results = document.getElementById("results")
+ results.innerHTML = (Math.round(sum * 100) / 100) + " GWh (" + Math.round(sum * 100 / 14.95 / pot.length) + "%)";
+ results.style.display = "block";
+
var geometry = new THREE.PlaneGeometry(this.width, this.height, 10 * this.width, 10 * this.height);
var colors = new three.BufferAttribute(new Float32Array(geometry.attributes.position.count * 4), 4);
geometry.setAttribute('color', colors);
@@ -190,8 +264,9 @@ class WindSimulation {
}
if(this.simulation)
- scene.remove(this.simulation);
+ this.resetSimulation();
this.simulation = mesh;
+ this.simulationPot = pot;
scene.add(mesh);
}
}
@@ -200,7 +275,7 @@ var width = 10;
var height = 10;
var ws = new WindSimulation(width, height, 5);
-var screen = new Screen(camera, renderer, new THREE.Vector3(width / 2 + 0.5, height / 2, -0.65));
+var screen = new Screen(camera, renderer, new THREE.Vector3(width / 2 + 0.5, height / 2, -0.3));
screen.setMoveCallBack((pos, end) => { ws.move(pos, end); });
screen.onResize();
@@ -232,6 +307,16 @@ window.light = light;
//scene.add( helper);
//window.helper = helper;
+
+
+
+
+window.windmill = WindMill;
+
+
+
+
+
window.ws = ws;
window.screen = screen;
window.scene = scene;
diff --git a/js/screen.js b/js/screen.js
index 023d49c..569dc9d 100644
--- a/js/screen.js
+++ b/js/screen.js
@@ -14,11 +14,11 @@ class Screen {
this.pointerScreen = new THREE.Vector2();
this.pointerWorld = new THREE.Vector3();
- this.radius = 24;
+ this.radius = 26;
this.phi = -0.5;
this.theta = 0.45;
this.z = 15;
- this.aspect = 2.13;
+ this.aspect = 1.94;
this.clicked = false;
diff --git a/js/spring.js b/js/spring.js
index cb92868..b2be7aa 100644
--- a/js/spring.js
+++ b/js/spring.js
@@ -1,7 +1,7 @@
import * as THREE from 'three';
// https://discourse.threejs.org/t/solved-create-a-spring/45135/9
-class Spring extends THREE.Mesh{
+class Spring extends THREE.Mesh {
constructor(radius, turns, segmentsPerTurn, height, growth, material){
let g = new THREE.CylinderGeometry(0.005, 0.005, 1, 16, segmentsPerTurn * turns).translate(0, 0.5, 0).rotateX(Math.PI * 0.5);
let initPos = g.attributes.position.clone();
diff --git a/js/windmill.js b/js/windmill.js
index 7251fc6..591dd6b 100644
--- a/js/windmill.js
+++ b/js/windmill.js
@@ -6,6 +6,7 @@ import { Spring } from './spring.js';
class WindMill extends THREE.Group {
static geometryBody = null;
static geometryBlades = null;
+ static geometryExclamation = null;
static {
const loader = new STLLoader();
@@ -16,15 +17,25 @@ class WindMill extends THREE.Group {
//loader.load('assets/cuerpo_126m_50.stl', function(geometry) {
WindMill.geometryBody = geometry;
});
+ loader.load('assets/exclamacion_50.stl', function(geometry) {
+ WindMill.geometryExclamation = geometry;
+ var colors = new three.BufferAttribute(new Float32Array(geometry.attributes.position.array.length), 3);
+ for(let i = 0; i < colors.count; i++)
+ if(Math.abs(geometry.attributes.position.getZ(i)) < 7.4)
+ colors.setXYZ(i, 0xfa / 0xff, 0xd5 / 0xff, 0x0a / 0xff);
+ WindMill.geometryExclamation.setAttribute('color', colors);
+ });
}
static isReady() {
- return WindMill.geometryBody != null && WindMill.geometryBlades != null;
+ return WindMill.geometryBody != null && WindMill.geometryBlades != null && WindMill.geometryExclamation != null;
}
constructor() {
super()
+ this.isLow = false;
+
this.material = new THREE.MeshStandardMaterial({color: 0xffffff, roughness: 0.15});
this.materialCircle = new THREE.MeshStandardMaterial({color: 0xaa0000});
//this.materialCircle.transparent = true;
@@ -84,20 +95,32 @@ class WindMill extends THREE.Group {
this.spring1.visible = this.spring2.visible = this.spring3.visible = false;
+ var material = new THREE.MeshStandardMaterial({color: 0xfad50a, roughness: 0.15, vertexColors: true});
+ this.meshExclamation = new THREE.Mesh(WindMill.geometryExclamation, material);
+ this.meshExclamation.castShadow = true;
+ //this.meshExclamation.receiveShadow = true;
+ this.meshExclamation.scale.setScalar(0.002);
+ this.meshExclamation.rotateX(Math.PI / 2);
+ this.meshExclamation.position.z = (90 + 126 / 2 + 1) / 126;
+ this.add(this.meshExclamation);
+ this.meshExclamation.visible = false;
+
this.selected = false;
}
- animate() {
- this.meshBlades.rotation.x += 0.1;
- this.spring1.rotation.x += 0.1;
- this.spring2.rotation.x += 0.1;
- this.spring3.rotation.x += 0.1;
+ animate(step) {
+ var inc = (this.isLow ? 0.3 : 3) * step;
+ this.meshBlades.rotation.x += inc;
+ this.spring1.rotation.x += inc;
+ this.spring2.rotation.x += inc;
+ this.spring3.rotation.x += inc;
+ this.meshExclamation.rotation.y += 4 * step;
}
select(selected) {
if(selected == this.selected) return;
this.selected = selected;
- this.meshCircle.visible = selected;
+ this.meshCircle.visible = selected;
if(selected)
this.material.color.set(0xff0000);
@@ -105,6 +128,13 @@ class WindMill extends THREE.Group {
this.material.color.set(0xffffff);
}
+ lowPower(isLow) {
+ if(this.isLow == isLow) return;
+ this.isLow = isLow;
+
+ this.meshExclamation.visible = isLow;
+ }
+
show_springs(value) {
this.spring1.visible = this.spring2.visible = this.spring3.visible = value;
}
diff --git a/server.py b/server.py
index 25386f8..961e36e 100644
--- a/server.py
+++ b/server.py
@@ -9,13 +9,19 @@ import xxx
def index():
if request.method != 'POST' or not request.data:
return abort(400);
+
params = json.loads(request.data.decode(encoding='utf-8'))
pos = params['pos']
direction = params['dir']
- ws = xxx.run(direction, pos);
+
+ if len(pos) > 10 or direction not in (0, 225, 270, 315):
+ return abort(400);
+
+ ws, pot = xxx.run(direction, pos);
return {
'pos': pos,
'ws': ws,
+ 'pot': pot,
'dir': direction,
}
diff --git a/style.css b/style.css
index e5fec23..b6f94a8 100644
--- a/style.css
+++ b/style.css
@@ -1,3 +1,8 @@
+* {
+ margin: 0;
+ padding: 0;
+}
+
body {
margin: 0;
font-family: "Encode Sans", sans-serif;
@@ -34,7 +39,7 @@ body {
}
#popup p + p {
- margin-top: -3vh;
+ margin-top: 1vh;
}
.l {
@@ -58,9 +63,7 @@ body {
}
#button {
- position: absolute;
- right: 4vh;
- top: 4vh;
+ display: inline-block;
padding: 4vh;
background: darkblue;
font-weight: 800;
@@ -70,24 +73,45 @@ body {
background: blue;
}
-#help {
+#right {
position: absolute;
- right: 4vh;
- top: 16vh;
+ max-width: 75vw;
+ text-align: right;
+ right: 1vh;
+ top: 1vh;
font-weight: 800;
font-size: .8em;
}
-#help li {
- display: block;
- float: left;
+#right li {
+ display: inline-block;
border: solid white .5vh;
- padding: .1vh 1vh;
border-radius: 1vh;
cursor: pointer;
- margin-left: 1vh;
- width: 2.5vh;
+ margin-bottom: 1vh;
+ width: 4vh;
+ height: 4vh;
text-align: center;
+ opacity: 0.5;
+}
+
+#right li.sel, #right li:hover {
+ opacity: 1;
+}
+
+#follower, #results {
+ border: solid white 1px;
+ z-index: 900;
+ position: absolute;
+ background: #00000088;
+ padding: 1vh;
+ font-size: .5em;
+ display: none;
+}
+
+#results {
+ bottom: 1vh;
+ left: 1vh;
}
#copyright {
diff --git a/xxx.py b/xxx.py
index 0a5bd90..bd46890 100644
--- a/xxx.py
+++ b/xxx.py
@@ -65,15 +65,15 @@ def py_wake_Initial_Cong(D,name,h,U_ref,initial_position=initial_position):
method='linear'))
-
-
-
-
windTurbines = py_wake_Initial_Cong(D, 'NREL_5MW', h, U_ref)
+grid = XYGrid(x=np.arange(0, 10.01, 0.1)*D, y=np.arange(0, 10.01, 0.1)*D)
def run(direction=0, initial_position=initial_position, U_ref=U_ref):
initial_position = np.array(initial_position);
- site = UniformSite(p_wd=[1] * 8,
+ p_wd = [0] * 360
+ #p_wd[direction * n // 360] = 1
+ p_wd[direction] = 1
+ site = UniformSite(p_wd=p_wd,
ws=U_ref,
initial_position=initial_position*D)
#ds = xr.Dataset(
@@ -84,7 +84,9 @@ def run(direction=0, initial_position=initial_position, U_ref=U_ref):
wt_x, wt_y = site.initial_position.T/D
wfm = PropagateDownwind(site, windTurbines, wake_deficitModel=TurboGaussianDeficit())
- grid = XYGrid(x=np.arange(0, 10.01, 0.1)*126, y=np.arange(0, 10.01, 0.1)*126)
- xa = wfm(x=wt_x*126, y=wt_y*126, wd=direction, yaw=0).flow_map(grid)
+ xa = wfm(x=wt_x*D, y=wt_y*D, wd=direction, yaw=0).flow_map(grid)
ws = xa.WS_eff
- return ws[:,:,0,0].values[:,:,0].tolist()
+
+ aep = wfm(x=wt_x*D, y=wt_y*D).aep()
+
+ return ws[:,:,0,0].values[:,:,0].tolist(), [sum(aep.values[i,:,0]) for i in aep.wt.values]