/*
* Copyright (C) 2010, 2011, 2012 by Arne Kesting, Martin Treiber, Ralph Germ, Martin Budden
* <movsim.org@gmail.com>
* -----------------------------------------------------------------------------------------
*
* This file is part of
*
* MovSim - the multi-model open-source vehicular-traffic simulator.
*
* MovSim is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MovSim is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with MovSim. If not, see <http://www.gnu.org/licenses/>
* or <http://www.movsim.org>.
*
* -----------------------------------------------------------------------------------------
*/
package org.movsim.consumption.model;
import org.movsim.autogen.ConsumptionModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Preconditions;
// TODO Fix output after refactoring
class EnergyFlowModelImpl implements EnergyFlowModel {
/** The Constant logger. */
private static final Logger logger = LoggerFactory.getLogger(EnergyFlowModelImpl.class);
/** if cons(m^3/(Ws)) higher, point (f,pe) out of Bounds 900=3-4 times the minimum */
private final double LIMIT_SPEC_CONS = 900 * ConsumptionConstants.CONVERSION_GRAMM_PER_KWH_TO_SI;
/** extremely high flow in motor regimes that cannot be reached. Set to 10000 KW */
private final double POW_ERROR = 1e7;
private final double FUELFLOW_ERROR = POW_ERROR * LIMIT_SPEC_CONS;
private final InstantaneousPowerModel carPowerModel;
private final EngineEfficienyModel engineModel;
private final EngineRotationModel engineRotationModel;
private final VehicleAttributes vehicle;
EnergyFlowModelImpl(String keyLabel, ConsumptionModel modelInput) {
Preconditions.checkNotNull(modelInput);
vehicle = new VehicleAttributes(modelInput.getVehicleData());
carPowerModel = new InstantaneousPowerModelImpl(vehicle);
engineRotationModel = new EngineRotationModel(modelInput.getRotationModel());
engineModel = new EngineEfficiencyModelAnalyticImpl(modelInput.getEngineCombustionMap(), engineRotationModel);
// TODO boolean type
if (modelInput.isOutput()) {
writeOutput(keyLabel);
}
}
double fuelflowError() {
return FUELFLOW_ERROR;
}
/**
* Gets the instantaneous fuel consumption in liters/100km with a cut-off for v=0.
*
* @param v
* @param acc
* @param gear
* @param withJante
* @return the instantaneous consumption per 100km
*/
@Override
public double getInstConsumption100km(double v, double acc, int gear, boolean withJante) {
final int gearIndex = gear - 1;
return (1e8 * getFuelFlow(v, acc, 0, gearIndex, withJante) / Math.max(v, 0.001));
}
/**
* Gets the fuel flow in m^3/s and the {code FUEL_ERROR} if the operation point is not possible.
*
* @param v
* @param acc
* @param gearIndex
* @param withJante
* @return fuelFlow
*/
@Override
public double getFuelFlow(double v, double acc, double grade, int gearIndex, boolean withJante) {
final double fMot = engineRotationModel.getEngineFrequency(v, gearIndex);
final double powMech = carPowerModel.getMechanicalPower(v, acc, grade);
// electric generator is not active near or at in standstill (v<1*3.6km/h)
// resulting in idle fuel consumption from engine specification
// modeling assumption becomes invalid if lot of standstills are considered
// electric consumption is active and no electric energy is provided by generator
final double elecPower = (v < 1) ? 0 : vehicle.electricPower();
final double powMechEl = powMech + elecPower;// can be <0
double fuelFlow = FUELFLOW_ERROR;
if (engineRotationModel.isFrequencyPossible(v, gearIndex) || gearIndex == 0) {
fuelFlow = engineModel.getFuelFlow(fMot, powMechEl);
}
// check if motor regime can be reached; otherwise increase fuelFlow prohibitively
// indicates that too high power required
if (powMech > engineModel.getMaxPower()) {
fuelFlow = FUELFLOW_ERROR;
}
// indicates that too high motor frequency
if (withJante && (fMot > engineRotationModel.getMaxFrequency())) {
if (logger.isDebugEnabled()) {
logger.debug(String
.format("v_kmh=%f, acc=%f, gear=%d, motor frequency=%d/min too high -- > return fuelErrorConsumption: %.2f",
(3.6 * v), acc, gearIndex + 1, (int) (fMot * 60), FUELFLOW_ERROR));
}
fuelFlow = FUELFLOW_ERROR;
}
// indicates too low motor frequency
if (withJante && (fMot < engineRotationModel.getMinFrequency())) {
if (gearIndex == 0) {
fuelFlow = vehicle.electricPower() * LIMIT_SPEC_CONS;
if (logger.isDebugEnabled()) {
logger.debug(String.format("v=%f, gear=%d, fuelFlow=%f %n", v, gearIndex + 1, fuelFlow));
}
} else {
fuelFlow = FUELFLOW_ERROR;
}
}
return fuelFlow;
}
/**
* Gets the optimum fuel consumption flow in m^3/s and the used fuel-optimized gear
*
* @param v
* @param acc
* @param grade
* in radians
* @param withJante
* @return fuelFlow and gear
*/
@Override
public double[] getMinFuelFlow(double v, double acc, double grade, boolean withJante) {
int gear = 1;
double fuelFlow = FUELFLOW_ERROR;
for (int testGearIndex = engineRotationModel.getMaxGearIndex(); testGearIndex >= 0; testGearIndex--) {
final double fuelFlowGear = getFuelFlow(v, acc, grade, testGearIndex, withJante);
if (fuelFlowGear < fuelFlow) {
gear = testGearIndex + 1;
fuelFlow = fuelFlowGear;
}
}
final double[] retValue = { fuelFlow, gear };
return retValue;
}
/**
* Gets the optimum fuel consumption flow in liter per s
*
* @param v
* @param acc
* @return the fuel flow in liter per s
*/
@Override
public double getFuelFlowInLiterPerS(double v, double acc) {
return 1000 * getMinFuelFlow(v, acc, 0, true)[0]; // convert from m^3/s --> liter/s
}
/**
* Returns the instantaneous fuel consumption flowin liter per second considering speed, acceleration and the gradient (in radians)
*
* @param v
* @param acc
* @param grade
* @return
*/
@Override
public double getFuelFlowInLiterPerS(double v, double acc, double grade) {
return 1000 * getMinFuelFlow(v, acc, grade, true)[0]; // convert from m^3/s --> liter/s
}
// TODO draw out
private void writeOutput(String keyLabel) {
final FileFuelConsumptionModel fileOutput = new FileFuelConsumptionModel(keyLabel, this);
fileOutput.writeJanteOptimalGear(vehicle, carPowerModel);
fileOutput.writeZeroAccelerationTest(vehicle, carPowerModel, engineRotationModel);
if(engineModel instanceof EngineEfficiencyModelAnalyticImpl){
fileOutput.writeSpecificConsumption(engineRotationModel, (EngineEfficiencyModelAnalyticImpl) engineModel);
}
// jante output per gear
// for (int gearIndex = 0; gearIndex < engineRotationModel.getMaxGearIndex(); gearIndex++) {
// final int gear = gearIndex + 1;
// final String strGear = new Integer(gear).toString();
// final String filename = projectName + ".Jante" + strGear;
// writeJanteDataFields(gear, filename);
// }
}
}