Search in sources :

Example 16 with Ray3

use of com.ardor3d.math.Ray3 in project energy3d by concord-consortium.

the class SolarRadiation method computeOnParabolicTrough.

// Unlike PV solar panels, no indirect (ambient or diffuse) radiation should be included in reflection calculation. The mesh is a parabolic surface.
private void computeOnParabolicTrough(final int minute, final ReadOnlyVector3 directionTowardSun, final ParabolicTrough trough) {
    final int nAxis = trough.getNSectionAxis();
    final int nPara = trough.getNSectionParabola();
    final Calendar calendar = Heliodon.getInstance().getCalendar();
    calendar.set(Calendar.HOUR_OF_DAY, (int) ((double) minute / (double) SolarRadiation.MINUTES_OF_DAY * 24.0));
    calendar.set(Calendar.MINUTE, minute % 60);
    trough.draw();
    final ReadOnlyVector3 normal = trough.getNormal();
    if (normal == null) {
        throw new RuntimeException("Normal is null");
    }
    // nx*ny*60: nx*ny is to get the unit cell area of the nx*ny grid; 60 is to convert the unit of timeStep from minute to kWh
    final double a = trough.getApertureWidth() * trough.getTroughLength() * Scene.getInstance().getTimeStep() / (nAxis * nPara * 60.0);
    final Mesh mesh = trough.getRadiationMesh();
    MeshDataStore data = onMesh.get(mesh);
    if (data == null) {
        // axis is row and parabola is column
        data = initMeshTextureDataOnRectangle(mesh, nAxis, nPara);
    }
    final double dot = normal.dot(directionTowardSun);
    double directRadiation = 0;
    if (dot > 0) {
        directRadiation += calculateDirectRadiation(directionTowardSun, normal);
    }
    final FloatBuffer vertexBuffer = mesh.getMeshData().getVertexBuffer();
    // number of vertex coordinates on each end
    final int j = vertexBuffer.limit() / 2;
    // (0, 0)
    final Vector3 p0 = new Vector3(vertexBuffer.get(0), vertexBuffer.get(1), vertexBuffer.get(2));
    // (1, 0)
    final Vector3 p1 = new Vector3(vertexBuffer.get(j - 3), vertexBuffer.get(j - 2), vertexBuffer.get(j - 1));
    // (0, 1)
    final Vector3 p2 = new Vector3(vertexBuffer.get(j), vertexBuffer.get(j + 1), vertexBuffer.get(j + 2));
    // final Vector3 q0 = mesh.localToWorld(p0, null);
    // final Vector3 q1 = mesh.localToWorld(p1, null);
    // final Vector3 q2 = mesh.localToWorld(p2, null);
    // System.out.println("***" + q0.distance(q1) * Scene.getInstance().getAnnotationScale() + "," + q0.distance(q2) * Scene.getInstance().getAnnotationScale());
    // this is perpendicular to the direction of the cylinder axis (nPara)
    final Vector3 u = p1.subtract(p0, null).normalizeLocal();
    // this is parallel to the direction of the cylinder axis (nAxis)
    final Vector3 v = p2.subtract(p0, null).normalizeLocal();
    final double xSpacing = p1.distance(p0) / nPara;
    final double ySpacing = p2.distance(p0) / nAxis;
    // as the parabolic trough always faces the sun, we only have to deal with its aperture plane
    final int iMinute = minute / Scene.getInstance().getTimeStep();
    for (int x = 0; x < nPara; x++) {
        for (int y = 0; y < nAxis; y++) {
            if (EnergyPanel.getInstance().isCancelled()) {
                throw new CancellationException();
            }
            final Vector3 u2 = u.multiply(xSpacing * (x + 0.5), null);
            final Vector3 v2 = v.multiply(ySpacing * (y + 0.5), null);
            // on the aperture plane of the parabolic trough
            final Vector3 q = p0.add(v2, null).addLocal(u2);
            final ReadOnlyVector3 p = mesh.localToWorld(q, null);
            final Ray3 pickRay = new Ray3(p, directionTowardSun);
            if (dot > 0) {
                final PickResults pickResults = new PrimitivePickResults();
                for (final Spatial spatial : collidables) {
                    if (spatial != mesh) {
                        PickingUtil.findPick(spatial, pickRay, pickResults, false);
                        if (pickResults.getNumber() != 0) {
                            break;
                        }
                    }
                }
                if (pickResults.getNumber() == 0) {
                    // for heat map generation
                    data.dailySolarIntensity[y][x] += directRadiation;
                    // sum all the solar energy up over all meshes and store in the foundation's solar potential array
                    trough.getSolarPotential()[iMinute] += directRadiation * a;
                }
            }
        }
    }
}
Also used : Calendar(java.util.Calendar) Mesh(com.ardor3d.scenegraph.Mesh) FloatBuffer(java.nio.FloatBuffer) ReadOnlyVector3(com.ardor3d.math.type.ReadOnlyVector3) Vector3(com.ardor3d.math.Vector3) CullHint(com.ardor3d.scenegraph.hint.CullHint) TPoint(org.poly2tri.triangulation.point.TPoint) Point(org.poly2tri.geometry.primitives.Point) Ray3(com.ardor3d.math.Ray3) PrimitivePickResults(com.ardor3d.intersection.PrimitivePickResults) ReadOnlyVector3(com.ardor3d.math.type.ReadOnlyVector3) CancellationException(java.util.concurrent.CancellationException) Spatial(com.ardor3d.scenegraph.Spatial) PrimitivePickResults(com.ardor3d.intersection.PrimitivePickResults) PickResults(com.ardor3d.intersection.PickResults)

Example 17 with Ray3

use of com.ardor3d.math.Ray3 in project energy3d by concord-consortium.

the class SolarRadiation method computeOnMesh.

// Formula from http://en.wikipedia.org/wiki/Air_mass_(solar_energy)#Solar_intensity
private void computeOnMesh(final int minute, final ReadOnlyVector3 directionTowardSun, final HousePart housePart, final Mesh drawMesh, final Mesh collisionMesh, final ReadOnlyVector3 normal) {
    if (normal == null) {
        // FIXME: normal can be null sometimes, fix it
        return;
    }
    if (Scene.getInstance().getOnlySolarComponentsInSolarMap()) {
        return;
    }
    MeshDataStore data = onMesh.get(drawMesh);
    if (data == null) {
        data = initMeshTextureData(drawMesh, collisionMesh, normal, !(housePart instanceof Window));
    }
    /* needed in order to prevent picking collision with neighboring wall at wall edge (seem 0.1 is too small, 0.5 is about right) */
    final ReadOnlyVector3 offset = directionTowardSun.multiply(0.5, null);
    final double dot = normal.dot(directionTowardSun);
    final double directRadiation = dot > 0 ? calculateDirectRadiation(directionTowardSun, normal) : 0;
    final double indirectRadiation = calculateDiffuseAndReflectedRadiation(directionTowardSun, normal);
    final int timeStep = Scene.getInstance().getTimeStep();
    final double solarStep = Scene.getInstance().getSolarStep();
    final double annotationScale = Scene.getInstance().getAnnotationScale();
    final double scaleFactor = annotationScale * annotationScale / 60 * timeStep;
    // a window itself doesn't really absorb solar energy, but it passes the energy into the house to be absorbed
    final float absorption = housePart instanceof Window ? 1 : 1 - housePart.getAlbedo();
    if (housePart instanceof Roof) {
        // for now, only store this for roofs that have different meshes
        if (data.solarPotential == null) {
            data.solarPotential = new double[MINUTES_OF_DAY / timeStep];
        }
        if (data.heatLoss == null) {
            data.heatLoss = new double[MINUTES_OF_DAY / timeStep];
        }
    }
    for (int col = 0; col < data.cols; col++) {
        final double w = col == data.cols - 1 ? data.p2.distance(data.u.multiply(col * solarStep, null).addLocal(data.p0)) : solarStep;
        final ReadOnlyVector3 pU = data.u.multiply(col * solarStep + 0.5 * w, null).addLocal(data.p0);
        for (int row = 0; row < data.rows; row++) {
            if (EnergyPanel.getInstance().isCancelled()) {
                throw new CancellationException();
            }
            if (data.dailySolarIntensity[row][col] == -1) {
                continue;
            }
            final double h = row == data.rows - 1 ? data.p1.distance(data.p0) - row * solarStep : solarStep;
            final ReadOnlyVector3 p = data.v.multiply(row * solarStep + 0.5 * h, null).addLocal(pU).add(offset, null);
            final Ray3 pickRay = new Ray3(p, directionTowardSun);
            final PickResults pickResults = new PrimitivePickResults();
            // assuming that indirect (ambient or diffuse) radiation can always reach a grid point
            double radiation = indirectRadiation;
            final double scaledArea = w * h * scaleFactor;
            if (dot > 0) {
                for (final Spatial spatial : collidables) {
                    if (EnergyPanel.getInstance().isCancelled()) {
                        throw new CancellationException();
                    }
                    if (spatial != collisionMesh) {
                        PickingUtil.findPick(spatial, pickRay, pickResults, false);
                        if (pickResults.getNumber() != 0) {
                            if (housePart instanceof Foundation) {
                                // at this point, we only show radiation heat map on the first floor
                                final HousePart collidableOwner = collidablesToParts.get(spatial);
                                if (collidableOwner instanceof Window) {
                                    radiation += directRadiation * ((Window) collidableOwner).getSolarHeatGainCoefficient();
                                }
                            }
                            break;
                        }
                    }
                }
                if (pickResults.getNumber() == 0) {
                    radiation += directRadiation;
                }
            }
            data.dailySolarIntensity[row][col] += Scene.getInstance().getOnlyAbsorptionInSolarMap() ? absorption * radiation : radiation;
            if (data.solarPotential != null) {
                data.solarPotential[minute / timeStep] += radiation * scaledArea;
            }
            if (!(housePart instanceof Foundation)) {
                // exclude radiation on foundation
                housePart.getSolarPotential()[minute / timeStep] += radiation * scaledArea;
            }
        }
    }
}
Also used : Window(org.concord.energy3d.model.Window) CullHint(com.ardor3d.scenegraph.hint.CullHint) TPoint(org.poly2tri.triangulation.point.TPoint) Point(org.poly2tri.geometry.primitives.Point) Ray3(com.ardor3d.math.Ray3) PrimitivePickResults(com.ardor3d.intersection.PrimitivePickResults) ReadOnlyVector3(com.ardor3d.math.type.ReadOnlyVector3) Roof(org.concord.energy3d.model.Roof) CancellationException(java.util.concurrent.CancellationException) Spatial(com.ardor3d.scenegraph.Spatial) PrimitivePickResults(com.ardor3d.intersection.PrimitivePickResults) PickResults(com.ardor3d.intersection.PickResults) Foundation(org.concord.energy3d.model.Foundation) HousePart(org.concord.energy3d.model.HousePart)

Example 18 with Ray3

use of com.ardor3d.math.Ray3 in project energy3d by concord-consortium.

the class SolarRadiation method computeOnLand.

private void computeOnLand(final ReadOnlyVector3 directionTowardSun) {
    final double indirectRadiation = calculateDiffuseAndReflectedRadiation(directionTowardSun, Vector3.UNIT_Z);
    final double totalRadiation = calculateDirectRadiation(directionTowardSun, Vector3.UNIT_Z) + indirectRadiation;
    final double step = Scene.getInstance().getSolarStep() * 4;
    final int rows = (int) (256 / step);
    final int cols = rows;
    MeshDataStore data = onMesh.get(SceneManager.getInstance().getSolarLand());
    if (data == null) {
        data = new MeshDataStore();
        data.dailySolarIntensity = new double[rows][cols];
        onMesh.put(SceneManager.getInstance().getSolarLand(), data);
    }
    final Vector3 p = new Vector3();
    final double absorption = 1 - Scene.getInstance().getGround().getAdjustedAlbedo(Heliodon.getInstance().getCalendar().get(Calendar.MONTH));
    for (int col = 0; col < cols; col++) {
        p.setX((col - cols / 2) * step + step / 2.0);
        for (int row = 0; row < rows; row++) {
            if (EnergyPanel.getInstance().isCancelled()) {
                throw new CancellationException();
            }
            p.setY((row - rows / 2) * step + step / 2.0);
            final Ray3 pickRay = new Ray3(p, directionTowardSun);
            final PickResults pickResults = new PrimitivePickResults();
            for (final Spatial spatial : collidables) {
                PickingUtil.findPick(spatial, pickRay, pickResults, false);
                if (pickResults.getNumber() != 0) {
                    break;
                }
            }
            if (pickResults.getNumber() == 0) {
                data.dailySolarIntensity[row][col] += Scene.getInstance().getOnlyAbsorptionInSolarMap() ? totalRadiation * absorption : totalRadiation;
            } else {
                // if shaded, it still receives indirect radiation
                data.dailySolarIntensity[row][col] += Scene.getInstance().getOnlyAbsorptionInSolarMap() ? indirectRadiation * absorption : indirectRadiation;
            }
        }
    }
}
Also used : PrimitivePickResults(com.ardor3d.intersection.PrimitivePickResults) CancellationException(java.util.concurrent.CancellationException) Spatial(com.ardor3d.scenegraph.Spatial) ReadOnlyVector3(com.ardor3d.math.type.ReadOnlyVector3) Vector3(com.ardor3d.math.Vector3) PrimitivePickResults(com.ardor3d.intersection.PrimitivePickResults) PickResults(com.ardor3d.intersection.PickResults) CullHint(com.ardor3d.scenegraph.hint.CullHint) TPoint(org.poly2tri.triangulation.point.TPoint) Point(org.poly2tri.geometry.primitives.Point) Ray3(com.ardor3d.math.Ray3)

Example 19 with Ray3

use of com.ardor3d.math.Ray3 in project energy3d by concord-consortium.

the class SolarRadiation method computeOnRack.

// TODO: we probably should handle the radiation heat map visualization on the rack using a coarse grid and the energy calculation using a fine grid
private void computeOnRack(final int minute, final ReadOnlyVector3 directionTowardSun, final Rack rack) {
    if (rack.getTracker() != SolarPanel.NO_TRACKER) {
        final Calendar calendar = Heliodon.getInstance().getCalendar();
        calendar.set(Calendar.HOUR_OF_DAY, (int) ((double) minute / (double) SolarRadiation.MINUTES_OF_DAY * 24.0));
        calendar.set(Calendar.MINUTE, minute % 60);
        rack.draw();
    }
    if (!rack.isMonolithic()) {
        return;
    }
    final ReadOnlyVector3 normal = rack.getNormal();
    if (normal == null) {
        throw new RuntimeException("Normal is null");
    }
    int nx = Scene.getInstance().getRackNx();
    int ny = Scene.getInstance().getRackNy();
    final Mesh drawMesh = rack.getRadiationMesh();
    final Mesh collisionMesh = (Mesh) rack.getRadiationCollisionSpatial();
    MeshDataStore data = onMesh.get(drawMesh);
    if (data == null) {
        data = initMeshTextureDataOnRectangle(drawMesh, nx, ny);
    }
    final ReadOnlyVector3 offset = directionTowardSun.multiply(1, null);
    final double dot = normal.dot(directionTowardSun);
    double directRadiation = 0;
    if (dot > 0) {
        directRadiation += calculateDirectRadiation(directionTowardSun, normal);
    }
    final double indirectRadiation = calculateDiffuseAndReflectedRadiation(directionTowardSun, normal);
    final FloatBuffer vertexBuffer = drawMesh.getMeshData().getVertexBuffer();
    // (0, 0)
    final Vector3 p0 = new Vector3(vertexBuffer.get(3), vertexBuffer.get(4), vertexBuffer.get(5));
    // (1, 0)
    final Vector3 p1 = new Vector3(vertexBuffer.get(6), vertexBuffer.get(7), vertexBuffer.get(8));
    // (0, 1)
    final Vector3 p2 = new Vector3(vertexBuffer.get(0), vertexBuffer.get(1), vertexBuffer.get(2));
    // this is the longer side (supposed to be y)
    final double d10 = p1.distance(p0);
    // this is the shorter side (supposed to be x)
    final double d20 = p2.distance(p0);
    final Vector3 p10 = p1.subtract(p0, null).normalizeLocal();
    final Vector3 p20 = p2.subtract(p0, null).normalizeLocal();
    // generate the heat map first. this doesn't affect the energy calculation, it just shows the distribution of solar radiation on the rack.
    // x and y must be swapped to have correct heat map texture, because nx represents rows and ny columns as we call initMeshTextureDataOnRectangle(mesh, nx, ny)
    double xSpacing = d10 / nx;
    double ySpacing = d20 / ny;
    Vector3 u = p10;
    Vector3 v = p20;
    final int iMinute = minute / Scene.getInstance().getTimeStep();
    for (int x = 0; x < nx; x++) {
        for (int y = 0; y < ny; y++) {
            if (EnergyPanel.getInstance().isCancelled()) {
                throw new CancellationException();
            }
            final Vector3 u2 = u.multiply(xSpacing * (x + 0.5), null);
            final Vector3 v2 = v.multiply(ySpacing * (y + 0.5), null);
            final ReadOnlyVector3 p = drawMesh.getWorldTransform().applyForward(p0.add(v2, null).addLocal(u2)).addLocal(offset);
            final Ray3 pickRay = new Ray3(p, directionTowardSun);
            // assuming that indirect (ambient or diffuse) radiation can always reach a grid point
            double radiation = indirectRadiation;
            if (dot > 0) {
                final PickResults pickResults = new PrimitivePickResults();
                for (final Spatial spatial : collidables) {
                    if (spatial != collisionMesh) {
                        PickingUtil.findPick(spatial, pickRay, pickResults, false);
                        if (pickResults.getNumber() != 0) {
                            break;
                        }
                    }
                }
                if (pickResults.getNumber() == 0) {
                    radiation += directRadiation;
                }
            }
            data.dailySolarIntensity[x][y] += radiation;
        }
    }
    // now do the calculation to get the total energy generated by the cells
    final double airTemperature = Weather.getInstance().getOutsideTemperatureAtMinute(dailyAirTemperatures[1], dailyAirTemperatures[0], minute);
    // system efficiency
    double syseff;
    // output at a cell center
    double output;
    // cell temperature
    double tcell;
    final SolarPanel panel = rack.getSolarPanel();
    if (Scene.getInstance().isRackModelExact()) {
        // exactly model each solar cell on each solar panel
        final int[] rc = rack.getSolarPanelRowAndColumnNumbers();
        // numbers of solar panels in x and y directions
        final int nxPanels = rc[0];
        final int nyPanels = rc[1];
        // numbers of solar cells on each panel in x and y directions
        int nxCells, nyCells;
        if (panel.isRotated()) {
            nxCells = panel.getNumberOfCellsInY();
            nyCells = panel.getNumberOfCellsInX();
        } else {
            nxCells = panel.getNumberOfCellsInX();
            nyCells = panel.getNumberOfCellsInY();
        }
        nx = nxCells * rc[0];
        ny = nyCells * rc[1];
        // get the area of a solar cell. 60 converts the unit of timeStep from minute to kWh
        final double a = panel.getPanelWidth() * panel.getPanelHeight() * Scene.getInstance().getTimeStep() / (panel.getNumberOfCellsInX() * panel.getNumberOfCellsInY() * 60.0);
        // swap the x and y back to correct order
        xSpacing = d20 / nx;
        ySpacing = d10 / ny;
        u = p20;
        v = p10;
        if (cellOutputs == null || cellOutputs.length != nx || cellOutputs[0].length != ny) {
            cellOutputs = new double[nx][ny];
        }
        // calculate the solar radiation first without worrying about the underlying cell wiring and distributed efficiency
        for (int x = 0; x < nx; x++) {
            for (int y = 0; y < ny; y++) {
                if (EnergyPanel.getInstance().isCancelled()) {
                    throw new CancellationException();
                }
                final Vector3 u2 = u.multiply(xSpacing * (x + 0.5), null);
                final Vector3 v2 = v.multiply(ySpacing * (y + 0.5), null);
                final ReadOnlyVector3 p = drawMesh.getWorldTransform().applyForward(p0.add(v2, null).addLocal(u2)).addLocal(offset);
                final Ray3 pickRay = new Ray3(p, directionTowardSun);
                // assuming that indirect (ambient or diffuse) radiation can always reach a grid point
                double radiation = indirectRadiation;
                if (dot > 0) {
                    final PickResults pickResults = new PrimitivePickResults();
                    for (final Spatial spatial : collidables) {
                        if (spatial != collisionMesh) {
                            PickingUtil.findPick(spatial, pickRay, pickResults, false);
                            if (pickResults.getNumber() != 0) {
                                break;
                            }
                        }
                    }
                    if (pickResults.getNumber() == 0) {
                        radiation += directRadiation;
                    }
                }
                cellOutputs[x][y] = radiation * a;
            }
        }
        // Tcell = Tair + (NOCT - 20) / 80 * R, where the unit of R is mW/cm^2
        final double noctFactor = (panel.getNominalOperatingCellTemperature() - 20.0) * 100.0 / (a * 80.0);
        // now consider cell wiring and distributed efficiency. TODO: This is very inaccurate. The output depends on both cell wiring and panel wiring.
        switch(// the ideal case that probably doesn't exist in reality
        panel.getShadeTolerance()) {
            case SolarPanel.HIGH_SHADE_TOLERANCE:
                for (int x = 0; x < nx; x++) {
                    for (int y = 0; y < ny; y++) {
                        output = cellOutputs[x][y];
                        tcell = airTemperature + output * noctFactor;
                        syseff = panel.getSystemEfficiency(tcell);
                        rack.getSolarPotential()[iMinute] += output * syseff;
                    }
                }
                break;
            case // assuming that all the cells on a panel are connected in series and all panels are connected in parallel
            SolarPanel.NO_SHADE_TOLERANCE:
                double min = Double.MAX_VALUE;
                for (int ix = 0; ix < nxPanels; ix++) {
                    // panel by panel
                    for (int iy = 0; iy < nyPanels; iy++) {
                        min = Double.MAX_VALUE;
                        for (int jx = 0; jx < nxCells; jx++) {
                            // cell by cell on each panel
                            for (int jy = 0; jy < nyCells; jy++) {
                                output = cellOutputs[ix * nxCells + jx][iy * nyCells + jy];
                                tcell = airTemperature + output * noctFactor;
                                syseff = panel.getSystemEfficiency(tcell);
                                output *= syseff;
                                if (output < min) {
                                    min = output;
                                }
                            }
                        }
                        rack.getSolarPotential()[iMinute] += min * nxCells * nyCells;
                    }
                }
                break;
            case // assuming each panel uses a diode bypass to connect two columns of cells
            SolarPanel.PARTIAL_SHADE_TOLERANCE:
                for (int ix = 0; ix < nxPanels; ix++) {
                    // panel by panel
                    for (int iy = 0; iy < nyPanels; iy++) {
                        min = Double.MAX_VALUE;
                        if (panel.isRotated()) {
                            // landscape: nxCells = 10, nyCells = 6
                            for (int jy = 0; jy < nyCells; jy++) {
                                // cell by cell on each panel
                                if (jy % 2 == 0) {
                                    // reset min every two columns of cells
                                    min = Double.MAX_VALUE;
                                }
                                for (int jx = 0; jx < nxCells; jx++) {
                                    output = cellOutputs[ix * nxCells + jx][iy * nyCells + jy];
                                    tcell = airTemperature + output * noctFactor;
                                    syseff = panel.getSystemEfficiency(tcell);
                                    output *= syseff;
                                    if (output < min) {
                                        min = output;
                                    }
                                }
                                if (jy % 2 == 1) {
                                    rack.getSolarPotential()[iMinute] += min * 2 * nxCells;
                                }
                            }
                        } else {
                            // portrait: nxCells = 6, nyCells = 10
                            for (int jx = 0; jx < nxCells; jx++) {
                                // cell by cell on each panel
                                if (jx % 2 == 0) {
                                    // reset min every two columns of cells
                                    min = Double.MAX_VALUE;
                                }
                                for (int jy = 0; jy < nyCells; jy++) {
                                    output = cellOutputs[ix * nxCells + jx][iy * nyCells + jy];
                                    tcell = airTemperature + output * noctFactor;
                                    syseff = panel.getSystemEfficiency(tcell);
                                    output *= syseff;
                                    if (output < min) {
                                        min = output;
                                    }
                                }
                                if (jx % 2 == 1) {
                                    rack.getSolarPotential()[iMinute] += min * 2 * nyCells;
                                }
                            }
                        }
                    }
                }
                break;
        }
    } else {
        // for simulation speed, approximate rack model doesn't compute panel by panel and cell by cell
        ySpacing = xSpacing = Scene.getInstance().getRackCellSize() / Scene.getInstance().getAnnotationScale();
        // swap the x and y back to correct order
        nx = Math.max(2, (int) (d20 / xSpacing));
        ny = Math.max(2, (int) (d10 / ySpacing));
        // nx*ny*60: dividing the total rack area by nx*ny gets the unit cell area of the nx*ny grid; 60 converts the unit of timeStep from minute to kWh
        final double a = rack.getRackWidth() * rack.getRackHeight() * Scene.getInstance().getTimeStep() / (nx * ny * 60.0);
        u = p20;
        v = p10;
        if (cellOutputs == null || cellOutputs.length != nx || cellOutputs[0].length != ny) {
            cellOutputs = new double[nx][ny];
        }
        // calculate the solar radiation first without worrying about the underlying cell wiring and distributed efficiency
        for (int x = 0; x < nx; x++) {
            for (int y = 0; y < ny; y++) {
                if (EnergyPanel.getInstance().isCancelled()) {
                    throw new CancellationException();
                }
                final Vector3 u2 = u.multiply(xSpacing * (x + 0.5), null);
                final Vector3 v2 = v.multiply(ySpacing * (y + 0.5), null);
                final ReadOnlyVector3 p = drawMesh.getWorldTransform().applyForward(p0.add(v2, null).addLocal(u2)).addLocal(offset);
                final Ray3 pickRay = new Ray3(p, directionTowardSun);
                // assuming that indirect (ambient or diffuse) radiation can always reach a grid point
                double radiation = indirectRadiation;
                if (dot > 0) {
                    final PickResults pickResults = new PrimitivePickResults();
                    for (final Spatial spatial : collidables) {
                        if (spatial != collisionMesh) {
                            PickingUtil.findPick(spatial, pickRay, pickResults, false);
                            if (pickResults.getNumber() != 0) {
                                break;
                            }
                        }
                    }
                    if (pickResults.getNumber() == 0) {
                        radiation += directRadiation;
                    }
                }
                cellOutputs[x][y] = radiation * a;
            }
        }
        // Tcell = Tair + (NOCT - 20) / 80 * R, where the unit of R is mW/cm^2
        final double noctFactor = (panel.getNominalOperatingCellTemperature() - 20.0) * 100.0 / (a * 80.0);
        // now consider cell wiring and distributed efficiency. TODO: This is very inaccurate. The output depends on both cell wiring and panel wiring.
        switch(panel.getShadeTolerance()) {
            case // the ideal case that probably doesn't exist in reality
            SolarPanel.HIGH_SHADE_TOLERANCE:
                for (int x = 0; x < nx; x++) {
                    for (int y = 0; y < ny; y++) {
                        output = cellOutputs[x][y];
                        tcell = airTemperature + output * noctFactor;
                        syseff = panel.getSystemEfficiency(tcell);
                        rack.getSolarPotential()[iMinute] += output * syseff;
                    }
                }
                break;
            case SolarPanel.NO_SHADE_TOLERANCE:
                double min = Double.MAX_VALUE;
                for (int x = 0; x < nx; x++) {
                    for (int y = 0; y < ny; y++) {
                        output = cellOutputs[x][y];
                        tcell = airTemperature + output * noctFactor;
                        syseff = panel.getSystemEfficiency(tcell);
                        output *= syseff;
                        if (output < min) {
                            min = output;
                        }
                    }
                }
                rack.getSolarPotential()[iMinute] += min * ny * nx;
                break;
            case SolarPanel.PARTIAL_SHADE_TOLERANCE:
                for (int x = 0; x < nx; x++) {
                    min = Double.MAX_VALUE;
                    for (int y = 0; y < ny; y++) {
                        output = cellOutputs[x][y];
                        tcell = airTemperature + output * noctFactor;
                        syseff = panel.getSystemEfficiency(tcell);
                        output *= syseff;
                        if (output < min) {
                            min = output;
                        }
                    }
                    rack.getSolarPotential()[iMinute] += min * ny;
                }
                break;
        }
    }
}
Also used : Calendar(java.util.Calendar) Mesh(com.ardor3d.scenegraph.Mesh) FloatBuffer(java.nio.FloatBuffer) ReadOnlyVector3(com.ardor3d.math.type.ReadOnlyVector3) Vector3(com.ardor3d.math.Vector3) CullHint(com.ardor3d.scenegraph.hint.CullHint) TPoint(org.poly2tri.triangulation.point.TPoint) Point(org.poly2tri.geometry.primitives.Point) Ray3(com.ardor3d.math.Ray3) PrimitivePickResults(com.ardor3d.intersection.PrimitivePickResults) ReadOnlyVector3(com.ardor3d.math.type.ReadOnlyVector3) CancellationException(java.util.concurrent.CancellationException) Spatial(com.ardor3d.scenegraph.Spatial) SolarPanel(org.concord.energy3d.model.SolarPanel) PrimitivePickResults(com.ardor3d.intersection.PrimitivePickResults) PickResults(com.ardor3d.intersection.PickResults)

Example 20 with Ray3

use of com.ardor3d.math.Ray3 in project energy3d by concord-consortium.

the class Heliodon method initMouse.

private void initMouse(final LogicalLayer logicalLayer) {
    logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.F), new TriggerAction() {

        @Override
        public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
            setSunRegionAlwaysVisible(!forceSunRegionOn);
        }
    }));
    logicalLayer.registerTrigger(new InputTrigger(new MouseButtonPressedCondition(MouseButton.LEFT), new TriggerAction() {

        @Override
        public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
            oldHourAngle = hourAngle;
            changeTimeAndDateCommand = new ChangeTimeAndDateWithHeliodonCommand(calendar.getTime());
            final int x = inputStates.getCurrent().getMouseState().getX();
            final int y = inputStates.getCurrent().getMouseState().getY();
            final Ray3 pickRay = SceneManager.getInstance().getCanvas().getCanvasRenderer().getCamera().getPickRay(new Vector2(x, y), false, null);
            pickResults.clear();
            PickingUtil.findPick(sun, pickRay, pickResults);
            if (pickResults.getNumber() != 0) {
                sunGrabbed = true;
            } else {
                sunGrabbed = false;
            }
            if (forceSunRegionOn) {
                selectDifferentDeclinationWithMouse = true;
            } else {
                selectDifferentDeclinationWithMouse = false;
            }
            SceneManager.getInstance().setMouseControlEnabled(!sunGrabbed);
        }
    }));
    logicalLayer.registerTrigger(new InputTrigger(new MouseButtonReleasedCondition(MouseButton.LEFT), new TriggerAction() {

        @Override
        public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
            sunGrabbed = false;
            if (!forceSunRegionOn) {
                sunRegion.getSceneHints().setCullHint(CullHint.Always);
            }
            SceneManager.getInstance().setMouseControlEnabled(true);
            if (!Util.isEqual(oldHourAngle, hourAngle) && changeTimeAndDateCommand != null) {
                SceneManager.getInstance().getUndoManager().addEdit(changeTimeAndDateCommand);
            }
        }
    }));
    logicalLayer.registerTrigger(new InputTrigger(new MouseMovedCondition(), new TriggerAction() {

        @Override
        public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
            if (!sunGrabbed) {
                return;
            }
            final MouseState mouse = inputStates.getCurrent().getMouseState();
            final Ray3 pickRay = SceneManager.getInstance().getCamera().getPickRay(new Vector2(mouse.getX(), mouse.getY()), false, null);
            pickResults.clear();
            PickingUtil.findPick(sunRegion, pickRay, pickResults);
            final Vector3 intersectionPoint;
            if (pickResults.getNumber() > 0) {
                final IntersectionRecord intersectionRecord = pickResults.getPickData(0).getIntersectionRecord();
                intersectionPoint = intersectionRecord.getIntersectionPoint(intersectionRecord.getClosestIntersection());
            } else {
                intersectionPoint = null;
            }
            double smallestDistance = Double.MAX_VALUE;
            int hourVertex = -1;
            int totalHourVertices = 0;
            final Vector3 newSunLocation = new Vector3();
            final Vector3 p = new Vector3();
            final Vector3 p_abs = new Vector3();
            final ReadOnlyTransform rootTansform = root.getTransform();
            if (!selectDifferentDeclinationWithMouse) {
                final FloatBuffer buf = sunPath.getMeshData().getVertexBuffer();
                buf.rewind();
                while (buf.hasRemaining()) {
                    p.set(buf.get(), buf.get(), buf.get());
                    rootTansform.applyForward(p, p_abs);
                    final double d;
                    d = pickRay.distanceSquared(p_abs, null);
                    if (d < smallestDistance) {
                        smallestDistance = d;
                        hourVertex = buf.position() / 3 - 1;
                        newSunLocation.set(p);
                    }
                }
                totalHourVertices = buf.limit() / 3;
            }
            if (smallestDistance > 5.0 * root.getTransform().getScale().getX() * root.getTransform().getScale().getX()) {
                selectDifferentDeclinationWithMouse = true;
            }
            boolean declinationChanged = false;
            if (selectDifferentDeclinationWithMouse) {
                sunRegion.getSceneHints().setCullHint(CullHint.Inherit);
                int rowCounter = 0;
                int resultRow = -1;
                final FloatBuffer buf = sunRegion.getMeshData().getVertexBuffer();
                buf.rewind();
                final double r = 5.0 / 2.0;
                final Vector3 prev = new Vector3();
                int quadVertexCounter = 0;
                final double maxVertexInRow = HOUR_DIVISIONS * 4.0;
                int rowVertexCounter = 0;
                boolean foundInThisRow = false;
                while (buf.hasRemaining()) {
                    p.set(buf.get(), buf.get(), buf.get());
                    rootTansform.applyForward(p, p_abs);
                    final double d;
                    if (intersectionPoint != null) {
                        d = intersectionPoint.distanceSquared(p_abs);
                    } else {
                        d = pickRay.distanceSquared(p_abs, null);
                    }
                    if (d < smallestDistance && p.getZ() >= -MathUtils.ZERO_TOLERANCE) {
                        smallestDistance = d;
                        newSunLocation.set(p);
                        resultRow = rowCounter + (quadVertexCounter >= 2 ? 1 : 0);
                        hourVertex = rowVertexCounter / 4 + (quadVertexCounter == 1 || quadVertexCounter == 2 ? 1 : 0);
                        foundInThisRow = true;
                    }
                    if (prev.lengthSquared() != 0 && (prev.distance(p) > r || rowVertexCounter >= maxVertexInRow)) {
                        rowCounter++;
                        if (foundInThisRow) {
                            totalHourVertices = rowVertexCounter / 4;
                        }
                        foundInThisRow = false;
                        rowVertexCounter = 0;
                    }
                    prev.set(p);
                    quadVertexCounter = (quadVertexCounter + 1) % 4;
                    rowVertexCounter++;
                }
                rowCounter++;
                if (resultRow != -1) {
                    if (rowCounter < DECLINATION_DIVISIONS && latitude > 0) {
                        resultRow += DECLINATION_DIVISIONS - rowCounter;
                    }
                    final double newDeclinationAngle = -TILT_ANGLE + (2.0 * TILT_ANGLE * resultRow / DECLINATION_DIVISIONS);
                    declinationChanged = !Util.isEqual(newDeclinationAngle, declinationAngle);
                    if (declinationChanged) {
                        setDeclinationAngle(newDeclinationAngle, false, true);
                        dirtySunPath = true;
                    }
                }
            }
            final double newHourAngle = (hourVertex - Math.floor(totalHourVertices / 2.0)) * Math.PI / 48.0;
            final boolean hourAngleChanged = !Util.isEqual(newHourAngle, hourAngle);
            if (hourAngleChanged) {
                setHourAngle(newHourAngle, false, true, false);
            }
            if (declinationChanged || hourAngleChanged) {
                setSunLocation(newSunLocation);
                drawSunTriangle();
                EnergyPanel.getInstance().updateRadiationHeatMap();
            }
        }
    }));
}
Also used : IntersectionRecord(com.ardor3d.intersection.IntersectionRecord) TriggerAction(com.ardor3d.input.logical.TriggerAction) MouseButtonReleasedCondition(com.ardor3d.input.logical.MouseButtonReleasedCondition) KeyPressedCondition(com.ardor3d.input.logical.KeyPressedCondition) MouseMovedCondition(com.ardor3d.input.logical.MouseMovedCondition) InputTrigger(com.ardor3d.input.logical.InputTrigger) Canvas(com.ardor3d.framework.Canvas) ReadOnlyTransform(com.ardor3d.math.type.ReadOnlyTransform) MouseButtonPressedCondition(com.ardor3d.input.logical.MouseButtonPressedCondition) ReadOnlyVector3(com.ardor3d.math.type.ReadOnlyVector3) Vector3(com.ardor3d.math.Vector3) FloatBuffer(java.nio.FloatBuffer) ChangeTimeAndDateWithHeliodonCommand(org.concord.energy3d.undo.ChangeTimeAndDateWithHeliodonCommand) CullHint(com.ardor3d.scenegraph.hint.CullHint) Ray3(com.ardor3d.math.Ray3) Vector2(com.ardor3d.math.Vector2) MouseState(com.ardor3d.input.MouseState) TwoInputStates(com.ardor3d.input.logical.TwoInputStates)

Aggregations

Ray3 (com.ardor3d.math.Ray3)24 PickResults (com.ardor3d.intersection.PickResults)18 PrimitivePickResults (com.ardor3d.intersection.PrimitivePickResults)18 ReadOnlyVector3 (com.ardor3d.math.type.ReadOnlyVector3)18 Vector3 (com.ardor3d.math.Vector3)15 Spatial (com.ardor3d.scenegraph.Spatial)15 CullHint (com.ardor3d.scenegraph.hint.CullHint)12 Point (org.poly2tri.geometry.primitives.Point)11 TPoint (org.poly2tri.triangulation.point.TPoint)11 Mesh (com.ardor3d.scenegraph.Mesh)10 CancellationException (java.util.concurrent.CancellationException)10 Vector2 (com.ardor3d.math.Vector2)9 FloatBuffer (java.nio.FloatBuffer)9 Calendar (java.util.Calendar)6 HousePart (org.concord.energy3d.model.HousePart)6 Node (com.ardor3d.scenegraph.Node)5 Foundation (org.concord.energy3d.model.Foundation)5 PickedHousePart (org.concord.energy3d.model.PickedHousePart)5 ReadOnlyVector2 (com.ardor3d.math.type.ReadOnlyVector2)3 Roof (org.concord.energy3d.model.Roof)3