Estimate panel temperature from ambient temperature and radiation

Additional parameter: temperature coefficient
Adapt efficiency according to these values
Bugfix input filter
Prepare V1.2
This commit is contained in:
woheller69 2023-04-13 08:52:54 +02:00
parent ab477f2cab
commit a1f14681fd
23 changed files with 139 additions and 145 deletions

View file

@ -14,6 +14,7 @@ public class SolarPowerPlant {
double cellsMaxPower;
double cellsArea;
double cellsEfficiency;
double cellsTempCoeff;
double diffuseEfficiency;
double inverterPowerLimit;
double inverterEfficiency;
@ -22,7 +23,7 @@ public class SolarPowerPlant {
private int[] shadingElevation;
private int[] shadingOpacity;
public SolarPowerPlant(double latitude, double longitude, double cellsMaxPower, double cellsArea, double cellsEfficiency, double diffuseEfficiency, double inverterPowerLimit, double inverterEfficiency, double azimuthAngle, double tiltAngle, int[] shadingElevation, int[] shadingOpacity ) {
public SolarPowerPlant(double latitude, double longitude, double cellsMaxPower, double cellsArea, double cellsEfficiency, double cellsTempCoeff, double diffuseEfficiency, double inverterPowerLimit, double inverterEfficiency, double azimuthAngle, double tiltAngle, int[] shadingElevation, int[] shadingOpacity ) {
this.latitude = latitude;
this.longitude = longitude;
this.cellsMaxPower = cellsMaxPower;
@ -35,10 +36,11 @@ public class SolarPowerPlant {
this.tiltAngle = tiltAngle;
this.shadingElevation = shadingElevation;
this.shadingOpacity = shadingOpacity;
this.cellsTempCoeff = cellsTempCoeff / 100;
}
public float getPower(double solarPowerNormal, double solarPowerDiffuse, long epochTimeSeconds) {
public float getPower(double solarPowerNormal, double solarPowerDiffuse, long epochTimeSeconds, float ambientTemperature) {
Instant i = Instant.ofEpochSecond(epochTimeSeconds); //currentTimeMillis is in GMT
ZonedDateTime dateTime = ZonedDateTime.ofInstant(i, ZoneId.of("GMT"));
@ -70,10 +72,23 @@ public class SolarPowerPlant {
}
}
}
double dcPower = (solarPowerNormal * efficiency + solarPowerDiffuse * diffuseEfficiency )* cellsEfficiency * cellsArea;
float totalRadiationOnCell = (float) (solarPowerNormal * efficiency + solarPowerDiffuse * diffuseEfficiency); //flat plate equivalent of the solar irradiance
float cellTemperature = calcCellTemperature(ambientTemperature,totalRadiationOnCell);
double dcPower = totalRadiationOnCell * cellsEfficiency * (1+(cellTemperature - 25)*cellsTempCoeff) * cellsArea;
double acPower = Math.min(dcPower * inverterEfficiency, inverterPowerLimit);
return (float) acPower;
}
public static float calcCellTemperature(float ambientTemperature, float totalIrradiance){
//models from here: https://www.scielo.br/j/babt/a/FBq5Pmm4gSFqsfh3V8MxfGN/ Photovoltaic Cell Temperature Estimation for a Grid-Connect Photovoltaic Systems in Curitiba
//float cellTemperature = 30.006f + 0.0175f*(totalIrradiance-300f)+1.14f*(ambientTemperature-25f); //Lasnier and Ang Lasnier, F.; Ang, T. G. Photovoltaic engineering handbook, 1st ed.; IOP Publishing LTD: Lasnier, France, 1990; pp. 258.
//float cellTemperature = ambientTemperature + 0.028f*totalIrradiance-1f; //Schott Schott, T. Operation temperatures of PV modules. Photovoltaic solar energy conference 1985, pp. 392-396.
float cellTemperature = ambientTemperature + 0.0342f*totalIrradiance; //Ross model: https://www.researchgate.net/publication/275438802_Thermal_effects_of_the_extended_holographic_regions_for_holographic_planar_concentrator
//assuming "not so well cooled" : 0.0342
return cellTemperature;
}
}

View file

@ -187,6 +187,7 @@ public class ManageLocationsActivity extends NavigationActivity {
EditText editCellsMaxPower = (EditText) dialogView.findViewById(R.id.EditLocation_Cell_Max_Power);
EditText editCellsArea = (EditText) dialogView.findViewById(R.id.EditLocation_Cells_Area);
EditText editCellsEfficiency = (EditText) dialogView.findViewById(R.id.EditLocation_Cell_Efficiency);
EditText editCellsTempCoeff = (EditText) dialogView.findViewById(R.id.EditLocation_Cell_Temp_Coeff);
EditText editDiffuseEfficiency = (EditText) dialogView.findViewById(R.id.EditLocation_Diffuse_Efficiency);
EditText editInverterPowerLimit = (EditText) dialogView.findViewById(R.id.EditLocation_Inverter_Power_Limit);
EditText editInverterEfficiency = (EditText) dialogView.findViewById(R.id.EditLocation_Inverter_Efficiency);
@ -204,6 +205,8 @@ public class ManageLocationsActivity extends NavigationActivity {
editCellsArea.setText(Float.toString(city.getCellsArea()));
editCellsEfficiency.setText(Float.toString(city.getCellsEfficiency()));
editCellsEfficiency.setFilters(new InputFilter[]{ new InputFilterMinMax(0, 100)});
editCellsTempCoeff.setText(Float.toString(city.getCellsTempCoeff()));
editCellsTempCoeff.setFilters(new InputFilter[]{ new InputFilterMinMax(-100, 100)});
editDiffuseEfficiency.setText(Float.toString(city.getDiffuseEfficiency()));
editDiffuseEfficiency.setFilters(new InputFilter[]{ new InputFilterMinMax(0, 100)});
editInverterPowerLimit.setText(Float.toString(city.getInverterPowerLimit()));
@ -237,6 +240,7 @@ public class ManageLocationsActivity extends NavigationActivity {
Float.parseFloat(editCellsMaxPower.getText().toString().isEmpty() ? "0" : editCellsMaxPower.getText().toString()),
Float.parseFloat(editCellsArea.getText().toString().isEmpty() ? "0" : editCellsArea.getText().toString()),
Float.parseFloat(editCellsEfficiency.getText().toString().isEmpty() ? "0" : editCellsEfficiency.getText().toString()),
Float.parseFloat(editCellsTempCoeff.getText().toString().isEmpty() ? "0" : editCellsTempCoeff.getText().toString()),
Float.parseFloat(editDiffuseEfficiency.getText().toString().isEmpty() ? "0" : editDiffuseEfficiency.getText().toString()),
Float.parseFloat(editInverterPowerLimit.getText().toString().isEmpty() ? "0" : editInverterPowerLimit.getText().toString()),
Float.parseFloat(editInverterEfficiency.getText().toString().isEmpty() ? "0" : editInverterEfficiency.getText().toString()),

View file

@ -17,6 +17,7 @@ public class CityToWatch {
private float cellsMaxPower;
private float cellsArea;
private float cellsEfficiency;
private float cellsTempCoeff;
private float diffuseEfficiency;
private float inverterPowerLimit;
private float inverterEfficiency;
@ -36,14 +37,15 @@ public class CityToWatch {
this.id = id;
this.cityId = cityId;
this.cityName = cityName;
this.cellsMaxPower=650;
this.cellsArea=3.18f;
this.cellsEfficiency=19.3f;
this.diffuseEfficiency=40;
this.inverterPowerLimit =600;
this.inverterEfficiency =95;
this.azimuthAngle=170;
this.tiltAngle =90;
this.cellsMaxPower = 650;
this.cellsArea = 3.18f;
this.cellsEfficiency = 19.3f;
this.cellsTempCoeff = -0.4f;
this.diffuseEfficiency = 40;
this.inverterPowerLimit = 600;
this.inverterEfficiency = 95;
this.azimuthAngle = 170;
this.tiltAngle = 90;
}
@ -182,4 +184,12 @@ public class CityToWatch {
public String getShadingOpacityString() {
return Arrays.toString(shadingOpacity).replaceAll("\\[|\\]|\\s", "");
}
public float getCellsTempCoeff() {
return cellsTempCoeff;
}
public void setCellsTempCoeff(float cellsTempCoeff) {
this.cellsTempCoeff = cellsTempCoeff;
}
}

View file

@ -20,7 +20,7 @@ import static androidx.core.app.JobIntentService.enqueueWork;
*/
public class SQLiteHelper extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 1;
private static final int DATABASE_VERSION = 2;
private Context context;
private List<City> allCities = new ArrayList<>();
@ -53,6 +53,7 @@ public class SQLiteHelper extends SQLiteOpenHelper {
private static final String CITIES_TO_WATCH_TILT_ANGLE = "tilt_angle";
private static final String CITIES_TO_WATCH_SHADING_ELEVATION = "shading_elevation";
private static final String CITIES_TO_WATCH_SHADING_OPACITY = "shading_opacity";
private static final String CITIES_TO_WATCH_CELLS_TEMP_COEFF = "cells_temp_coeff";
//Names of columns in TABLE_FORECAST
private static final String FORECAST_ID = "forecast_id";
@ -135,7 +136,8 @@ public class SQLiteHelper extends SQLiteOpenHelper {
CITIES_TO_WATCH_AZIMUTH_ANGLE + " REAL NOT NULL," +
CITIES_TO_WATCH_TILT_ANGLE + " REAL NOT NULL," +
CITIES_TO_WATCH_SHADING_ELEVATION + " VARCHAR(255) NOT NULL," +
CITIES_TO_WATCH_SHADING_OPACITY + " VARCHAR(255) NOT NULL)";
CITIES_TO_WATCH_SHADING_OPACITY + " VARCHAR(255) NOT NULL," +
CITIES_TO_WATCH_CELLS_TEMP_COEFF + " REAL NOT NULL)";
public static SQLiteHelper getInstance(Context context) {
if (instance == null && context != null) {
@ -160,6 +162,12 @@ public class SQLiteHelper extends SQLiteOpenHelper {
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
switch(oldVersion) {
case 1:
db.execSQL("ALTER TABLE "+TABLE_CITIES_TO_WATCH+" ADD COLUMN "+CITIES_TO_WATCH_CELLS_TEMP_COEFF+" REAL DEFAULT 0");
// we want both updates, so no break statement here...
}
}
@ -185,6 +193,7 @@ public class SQLiteHelper extends SQLiteOpenHelper {
values.put(CITIES_TO_WATCH_TILT_ANGLE,city.getTiltAngle());
values.put(CITIES_TO_WATCH_SHADING_ELEVATION,city.getShadingElevationString());
values.put(CITIES_TO_WATCH_SHADING_OPACITY,city.getShadingOpacityString());
values.put(CITIES_TO_WATCH_CELLS_TEMP_COEFF,city.getCellsTempCoeff());
long id=database.insert(TABLE_CITIES_TO_WATCH, null, values);
@ -218,6 +227,7 @@ public class SQLiteHelper extends SQLiteOpenHelper {
", " + CITIES_TO_WATCH_TILT_ANGLE +
", " + CITIES_TO_WATCH_SHADING_ELEVATION +
", " + CITIES_TO_WATCH_SHADING_OPACITY +
", " + CITIES_TO_WATCH_CELLS_TEMP_COEFF +
", " + CITIES_TO_WATCH_COLUMN_RANK +
" FROM " + TABLE_CITIES_TO_WATCH +
" WHERE " + CITIES_TO_WATCH_CITY_ID + " = ?", arguments);
@ -240,7 +250,8 @@ public class SQLiteHelper extends SQLiteOpenHelper {
cityToWatch.setTiltAngle(Float.parseFloat(cursor.getString(12)));
cityToWatch.setShadingElevation(cursor.getString(13));
cityToWatch.setShadingOpacity(cursor.getString(14));
cityToWatch.setRank(Integer.parseInt(cursor.getString(15)));
cityToWatch.setCellsTempCoeff(Float.parseFloat(cursor.getString(15)));
cityToWatch.setRank(Integer.parseInt(cursor.getString(16)));
cursor.close();
}
@ -271,6 +282,7 @@ public class SQLiteHelper extends SQLiteOpenHelper {
", " + CITIES_TO_WATCH_TILT_ANGLE +
", " + CITIES_TO_WATCH_SHADING_ELEVATION +
", " + CITIES_TO_WATCH_SHADING_OPACITY +
", " + CITIES_TO_WATCH_CELLS_TEMP_COEFF +
", " + CITIES_TO_WATCH_COLUMN_RANK +
" FROM " + TABLE_CITIES_TO_WATCH
, new String[]{});
@ -295,7 +307,8 @@ public class SQLiteHelper extends SQLiteOpenHelper {
cityToWatch.setTiltAngle(Float.parseFloat(cursor.getString(12)));
cityToWatch.setShadingElevation(cursor.getString(13));
cityToWatch.setShadingOpacity(cursor.getString(14));
cityToWatch.setRank(Integer.parseInt(cursor.getString(15)));
cityToWatch.setCellsTempCoeff(Float.parseFloat(cursor.getString(15)));
cityToWatch.setRank(Integer.parseInt(cursor.getString(16)));
cityToWatchList.add(cityToWatch);
} while (cursor.moveToNext());
@ -325,6 +338,7 @@ public class SQLiteHelper extends SQLiteOpenHelper {
values.put(CITIES_TO_WATCH_TILT_ANGLE,cityToWatch.getTiltAngle());
values.put(CITIES_TO_WATCH_SHADING_ELEVATION,cityToWatch.getShadingElevationString());
values.put(CITIES_TO_WATCH_SHADING_OPACITY,cityToWatch.getShadingOpacityString());
values.put(CITIES_TO_WATCH_CELLS_TEMP_COEFF,cityToWatch.getCellsTempCoeff());
database.update(TABLE_CITIES_TO_WATCH, values, CITIES_TO_WATCH_ID + " = ?",
new String[]{String.valueOf(cityToWatch.getId())});

View file

@ -5,9 +5,9 @@ import android.text.Spanned;
public class InputFilterMinMax implements InputFilter {
private int min, max;
private float min, max;
public InputFilterMinMax(int min, int max) {
public InputFilterMinMax(float min, float max) {
this.min = min;
this.max = max;
}
@ -15,17 +15,30 @@ public class InputFilterMinMax implements InputFilter {
@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
String oldString = dest.toString();
String insertString = source.toString();
String newString = new StringBuilder(oldString).insert(dstart,insertString).toString();
float input = Float.parseFloat(newString);
if (isInRange(min, max, input))
return null;
else
return "";
try {
String oldString = dest.toString();
String insertString = source.toString();
String newString = oldString.substring(0, dstart) + oldString.substring(dend);
newString = newString.substring(0, dstart) + insertString + newString.substring(dstart);
float input = Float.parseFloat(newString);
if (isInRange(min, max, input)) {
return null;
} else {
if (source.equals("") && dest.toString().length() != 1) {
//backspace was clicked, do not accept that change, unless user is deleting the last char
return dest.subSequence(dstart, dend);
} else {
return "";
}
}
} catch (NumberFormatException e) {
e.printStackTrace();
}
return "";
}
private boolean isInRange(int a, int b, float c) {
private boolean isInRange(float a, float b, float c) {
return b > a ? c >= a && c <= b : c >= b && c <= a;
}

View file

@ -23,6 +23,7 @@ public class ItemViewHolder extends RecyclerView.ViewHolder {
public TextView cellsMaxPower;
public TextView cellsArea;
public TextView cellsEfficiency;
public TextView cellsTempCoeff;
public TextView diffuseEfficiency;
public TextView inverterPowerLimit;
public TextView inverterEfficiency;
@ -41,6 +42,7 @@ public class ItemViewHolder extends RecyclerView.ViewHolder {
this.cellsMaxPower = (TextView) itemView.findViewById(R.id.city_cells_max_power);
this.cellsArea = (TextView) itemView.findViewById(R.id.city_cells_area);
this.cellsEfficiency = (TextView) itemView.findViewById(R.id.city_cells_efficiency);
this.cellsTempCoeff = (TextView) itemView.findViewById(R.id.city_cells_temp_coeff);
this.diffuseEfficiency = (TextView) itemView.findViewById(R.id.city_diffuse_efficiency);
this.inverterPowerLimit = (TextView) itemView.findViewById(R.id.city_inverter_power_limit);
this.inverterEfficiency = (TextView) itemView.findViewById(R.id.city_inverter_efficiency);

View file

@ -63,6 +63,7 @@ public class RecyclerOverviewListAdapter extends RecyclerView.Adapter<ItemViewHo
holder.tiltAngle.setText(context.getString(R.string.edit_location_hint_tilt) +": "+ cities.get(position).getTiltAngle());
holder.cellsMaxPower.setText(context.getString(R.string.edit_location_hint_cells_max_power) +": "+ cities.get(position).getCellsMaxPower());
holder.cellsEfficiency.setText(context.getString(R.string.edit_location_hint_cells_efficiency) +": "+ cities.get(position).getCellsEfficiency());
holder.cellsTempCoeff.setText(context.getString(R.string.edit_location_hint_cells_temp_coeff) +": "+ cities.get(position).getCellsTempCoeff());
holder.cellsArea.setText(context.getString(R.string.edit_location_hint_cells_area) +": "+ cities.get(position).getCellsArea());
holder.diffuseEfficiency.setText(context.getString(R.string.edit_location_hint_diffuse_efficiency) +": "+ cities.get(position).getDiffuseEfficiency());
holder.inverterPowerLimit.setText(context.getString(R.string.edit_location_hint_inverter_power_limit) +": "+ cities.get(position).getInverterPowerLimit());
@ -114,7 +115,7 @@ public class RecyclerOverviewListAdapter extends RecyclerView.Adapter<ItemViewHo
public CityToWatch getCitytoWatch(int position){
return cities.get(position);
}
public void updateCity(CityToWatch cityToWatch, String cityName, float latitude, float longitude, float azimuth, float tilt, float cellsMaxPower, float cellsArea, float cellsEfficiency, float diffuseEfficiency, float inverterPowerLimit, float inverterEfficiency, int[] shadingElevation, int[] shadingOpacity) {
public void updateCity(CityToWatch cityToWatch, String cityName, float latitude, float longitude, float azimuth, float tilt, float cellsMaxPower, float cellsArea, float cellsEfficiency, float cellsTempCoeff, float diffuseEfficiency, float inverterPowerLimit, float inverterEfficiency, int[] shadingElevation, int[] shadingOpacity) {
cityToWatch.setCityName(cityName);
cityToWatch.setLatitude(latitude);
cityToWatch.setLongitude(longitude);
@ -123,6 +124,7 @@ public class RecyclerOverviewListAdapter extends RecyclerView.Adapter<ItemViewHo
cityToWatch.setCellsMaxPower(cellsMaxPower);
cityToWatch.setCellsArea(cellsArea);
cityToWatch.setCellsEfficiency(cellsEfficiency);
cityToWatch.setCellsTempCoeff(cellsTempCoeff);
cityToWatch.setDiffuseEfficiency(diffuseEfficiency);
cityToWatch.setInverterPowerLimit(inverterPowerLimit);
cityToWatch.setInverterEfficiency(inverterEfficiency);

View file

@ -66,6 +66,7 @@ public class OMDataExtractor implements IDataExtractor {
JSONObject jsonData = new JSONObject(data);
JSONArray timeArray = jsonData.getJSONArray("time");
JSONArray weathercodeArray = jsonData.has("weathercode") ? jsonData.getJSONArray("weathercode") : null;
JSONArray tempArray = jsonData.has("temperature_2m") ? jsonData.getJSONArray("temperature_2m") : null;
JSONArray directRadiationArray = jsonData.has("direct_normal_irradiance") ? jsonData.getJSONArray("direct_normal_irradiance") : null;
JSONArray diffuseRadiationArray = jsonData.has("diffuse_radiation") ? jsonData.getJSONArray("diffuse_radiation") : null;
@ -73,18 +74,20 @@ public class OMDataExtractor implements IDataExtractor {
SQLiteHelper dbhelper = SQLiteHelper.getInstance(context);
CityToWatch city = dbhelper.getCityToWatch(cityID);
SolarPowerPlant spp = new SolarPowerPlant(city.getLatitude(), city.getLongitude(), city.getCellsMaxPower(), city.getCellsArea(), city.getCellsEfficiency(),city.getDiffuseEfficiency(), city.getInverterPowerLimit(), city.getInverterEfficiency(), city.getAzimuthAngle(), city.getTiltAngle(), city.getShadingElevation(), city.getShadingOpacity());
SolarPowerPlant spp = new SolarPowerPlant(city.getLatitude(), city.getLongitude(), city.getCellsMaxPower(), city.getCellsArea(), city.getCellsEfficiency(), city.getCellsTempCoeff(), city.getDiffuseEfficiency(), city.getInverterPowerLimit(), city.getInverterEfficiency(), city.getAzimuthAngle(), city.getTiltAngle(), city.getShadingElevation(), city.getShadingOpacity());
IApiToDatabaseConversion conversion = new OMToDatabaseConversion();
float ambientTemperature = 25;
for (int i = 0; i < timeArray.length(); i++) {
HourlyForecast hourlyForecast = new HourlyForecast();
hourlyForecast.setTimestamp(System.currentTimeMillis() / 1000);
if (timeArray!=null && !timeArray.isNull(i)) hourlyForecast.setForecastTime(timeArray.getLong(i)*1000L);
if (tempArray != null && !tempArray.isNull(i)) ambientTemperature = (float) tempArray.getDouble(i);
if (weathercodeArray!=null && !weathercodeArray.isNull(i)) hourlyForecast.setWeatherID(conversion.convertWeatherCategory(weathercodeArray.getString(i)));
if (directRadiationArray!=null && !directRadiationArray.isNull(i)) hourlyForecast.setDirectRadiationNormal((float) directRadiationArray.getDouble(i));
if (diffuseRadiationArray!=null && !diffuseRadiationArray.isNull(i)) hourlyForecast.setDiffuseRadiation((float) diffuseRadiationArray.getDouble(i));
hourlyForecast.setPower(spp.getPower(hourlyForecast.getDirectRadiationNormal(),hourlyForecast.getDiffuseRadiation(), timeArray.getLong(i)-1800)); //use solar position 1/2h earlier for calculation of average power in preceding hour
hourlyForecast.setPower(spp.getPower(hourlyForecast.getDirectRadiationNormal(),hourlyForecast.getDiffuseRadiation(), timeArray.getLong(i)-1800 , ambientTemperature)); //use solar position 1/2h earlier for calculation of average power in preceding hour
hourlyForecasts.add(hourlyForecast);
}
return hourlyForecasts;

View file

@ -24,7 +24,7 @@ public class OMHttpRequest {
SharedPreferences sharedPreferences=PreferenceManager.getDefaultSharedPreferences(context);
return String.format(
"%sforecast?latitude=%s&longitude=%s&forecast_days=%s&hourly=diffuse_radiation,direct_normal_irradiance,weathercode&daily=weathercode,sunrise,sunset,&timeformat=unixtime&timezone=auto",
"%sforecast?latitude=%s&longitude=%s&forecast_days=%s&hourly=temperature_2m,diffuse_radiation,direct_normal_irradiance,weathercode&daily=weathercode,sunrise,sunset,&timeformat=unixtime&timezone=auto",
BuildConfig.BASE_URL,
lat,
lon,