PVLIB_Ruby 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +3 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +21 -0
- data/PVLIB_Ruby.gemspec +35 -0
- data/README.md +41 -0
- data/Rakefile +1 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/examples/data/sandia_inverter_example.csv +3 -0
- data/examples/data/sandia_module_example.csv +3 -0
- data/examples/pv_power_generation_example.rb +120 -0
- data/lib/PVLIB_Ruby/air_mass.rb +26 -0
- data/lib/PVLIB_Ruby/calculation_helper.rb +43 -0
- data/lib/PVLIB_Ruby/dc_to_ac_conversion.rb +40 -0
- data/lib/PVLIB_Ruby/models/active_csv.rb +60 -0
- data/lib/PVLIB_Ruby/models/inverter.rb +46 -0
- data/lib/PVLIB_Ruby/models/location.rb +11 -0
- data/lib/PVLIB_Ruby/models/pv_module.rb +100 -0
- data/lib/PVLIB_Ruby/plain_of_array_irradiance.rb +125 -0
- data/lib/PVLIB_Ruby/pv_performance_characterization.rb +111 -0
- data/lib/PVLIB_Ruby/pv_temperature.rb +26 -0
- data/lib/PVLIB_Ruby/solar_ephemeris.rb +145 -0
- data/lib/PVLIB_Ruby/version.rb +3 -0
- data/lib/PVLIB_Ruby.rb +17 -0
- metadata +130 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7d3ae3a823ad98dd6eaad6afeea6fc3cc3ff5902
|
4
|
+
data.tar.gz: 9791749f4b811b64481206aec941f5289d1d543c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8043fbd98a76d68a1deb94449201eb34fca2365d54c5055878637531f62b445d236f810c85245de32b6774a25dd34fe3aa49e02102bc4490df4c35445ee9d0d2
|
7
|
+
data.tar.gz: d920617b1a837ffd83d763d3b921ab0bee605aa8aa575564eb390a22f63613c5f587d9e7e628fe33636c05895f45e04c0f9eac33a7bf6f1a0f3085b146406c2e
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
PVLIB_Ruby
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.2.2
|
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
4
|
+
|
5
|
+
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
|
6
|
+
|
7
|
+
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
8
|
+
|
9
|
+
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
|
10
|
+
|
11
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
|
12
|
+
|
13
|
+
This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Tadatoshi Takahashi
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/PVLIB_Ruby.gemspec
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'pvlib_ruby/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "PVLIB_Ruby"
|
8
|
+
spec.version = PvlibRuby::VERSION
|
9
|
+
spec.authors = ["Tadatoshi Takahashi"]
|
10
|
+
spec.email = ["tadatoshi@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{PVLIB written in Ruby.}
|
13
|
+
spec.description = %q{PVLIB_MatLab translated to Ruby code in Object-Oriented way.}
|
14
|
+
spec.homepage = "https://github.com/tadatoshi/PVLIB_Ruby"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
# Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
|
18
|
+
# delete this section to allow pushing this gem to any host.
|
19
|
+
if spec.respond_to?(:metadata)
|
20
|
+
spec.metadata['allowed_push_host'] = "https://rubygems.org"
|
21
|
+
else
|
22
|
+
raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
|
23
|
+
end
|
24
|
+
|
25
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
26
|
+
spec.bindir = "exe"
|
27
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
28
|
+
spec.require_paths = ["lib"]
|
29
|
+
|
30
|
+
spec.add_dependency "activemodel"
|
31
|
+
|
32
|
+
spec.add_development_dependency "bundler", "~> 1.9"
|
33
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
34
|
+
spec.add_development_dependency "rspec"
|
35
|
+
end
|
data/README.md
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# PVLIB_Ruby
|
2
|
+
|
3
|
+
PVLIB_MatLab translated to Ruby code in Object-Oriented way.
|
4
|
+
|
5
|
+
Since the naming convention is PVLIB_[language name], e.g. PVLIB_MatLab, PVLIB_Python, this gem is following the same naming convention, i.e. PVLIB_Ruby.
|
6
|
+
|
7
|
+
However, internally, this gem is following Ruby naming convention. Hence, the code is under 'lib/pvlib_ruby'.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'PVLIB_Ruby'
|
15
|
+
```
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install PVLIB_Ruby
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
See examples under examples directory.
|
28
|
+
|
29
|
+
## Development
|
30
|
+
|
31
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
|
32
|
+
|
33
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
34
|
+
|
35
|
+
## Contributing
|
36
|
+
|
37
|
+
1. Fork it ( https://github.com/[my-github-username]/PVLIB_Ruby/fork )
|
38
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
39
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
40
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
41
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "pvlib_ruby"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
@@ -0,0 +1,3 @@
|
|
1
|
+
"name","Vac","Pac0","Pdc0","Vdc0","Ps0","C0","C1","C2","C3","Pnt","Vdcmax","Idcmax","MPPTLow","MPPTHi","LibraryType","LibraryName",
|
2
|
+
"SunPower Corp (Original Mfg - PV Powered): SPR-2500 240V [CEC 2006]",240,2500,2630.0909,219.2317,41.3618,-1.265e-05,6.6747e-05,0.0017417,0.00061375,3.9,500,20,140,450,"SandiaInverter","Sandia Inverters",
|
3
|
+
|
@@ -0,0 +1,3 @@
|
|
1
|
+
"name","vintage","material","area","AlphaIsc","AlphaImp","Isc0","Imp0","Voc0","Vmp0","BetaVoc","BetaVmp","mBetaVoc","mBetaVmp","Ns","Np","delT","fd","n","Ix0","Ixx0","a_wind","b_wind","c",,,,,,,,"a",,,,,"b",,,,,,
|
2
|
+
"Sample Module",2006,"c-Si",1.244,0.000232,-0.00036,5.988,5.56,48.53,40.03,-0.152,-0.162,0,0,72,1,3,1,1.241,5.93,4.12,-3.62,-0.075,1.0072,-0.0072,0.32304,-3.4984,0.9966,0.0034,1.0827,-0.0827,-0.0001223,0.002416,-0.01912,0.07365,0.9259,-2.99e-09,5.35e-07,-3.4e-05,0.000862,-0.00699,1,
|
3
|
+
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# NOTE: PVLIB_Ruby gem must be installed in local machine in order to run this code.
|
2
|
+
# Execute "rake install" to use the code in this gem.
|
3
|
+
# Or if you use the gem downloaded from RubyGems.org, you don't have to execute "rake install" and just do "gem install PVLIB_Ruby"
|
4
|
+
|
5
|
+
require 'pvlib_ruby'
|
6
|
+
|
7
|
+
require 'bigdecimal'
|
8
|
+
|
9
|
+
DATA_DIRECTORY = File.expand_path('../data', __FILE__)
|
10
|
+
|
11
|
+
# for 360th row in PVSC40Tutorial_Master (360/30 = 12, i.e. noon, note: measurement is every two minutes)
|
12
|
+
direct_normal_irradiance = BigDecimal('631.3100')
|
13
|
+
global_horizontal_irradiance = BigDecimal('862.0619')
|
14
|
+
diffuse_horizontal_irradiance = BigDecimal('405.3100')
|
15
|
+
day_of_year = BigDecimal('294')
|
16
|
+
utc_offset = '-07:00'
|
17
|
+
# TODO: Get time from year and day_of_year:
|
18
|
+
time = Time.new(2008, 10, 20, 11, 58, 12, utc_offset)
|
19
|
+
albedo = BigDecimal('0.1500')
|
20
|
+
array_tilt = BigDecimal('35')
|
21
|
+
array_azimuth = BigDecimal('180')
|
22
|
+
latitude = BigDecimal('35.0500')
|
23
|
+
longitude = BigDecimal('-106.5400')
|
24
|
+
altitude = BigDecimal('1660')
|
25
|
+
location = Location.new(latitude, longitude, altitude)
|
26
|
+
pressure = BigDecimal('62963')
|
27
|
+
reference_solar_irradiance = BigDecimal('1000')
|
28
|
+
wind_speed = BigDecimal('2.8786')
|
29
|
+
air_temperature = BigDecimal('20.7700')
|
30
|
+
|
31
|
+
solar_ephemeris = SolarEphemeris.new(time, location, pressure: pressure, temperature: air_temperature)
|
32
|
+
|
33
|
+
sun_azimuth = solar_ephemeris.sun_azimuth
|
34
|
+
apparent_sun_elevation = solar_ephemeris.apparent_sun_elevation
|
35
|
+
|
36
|
+
puts "------------- Solar Ephemeris -------------"
|
37
|
+
puts " Sun Azimuth [º]: #{sun_azimuth.round(4).to_s('F')}" # Slight difference from 182.5229 for 360th row in PVSC40Tutorial_Master
|
38
|
+
puts " Apparent Sun Elevation [º]: #{apparent_sun_elevation.round(4).to_s('F')}" # Slight difference from 44.2308 for 360th row in PVSC40Tutorial_Master
|
39
|
+
puts "-------------------------------------------"
|
40
|
+
puts ""
|
41
|
+
|
42
|
+
sun_zenith = BigDecimal('90') - apparent_sun_elevation
|
43
|
+
|
44
|
+
plain_of_array_irradiance = PlainOfArrayIrradiance.new(direct_normal_irradiance, global_horizontal_irradiance, diffuse_horizontal_irradiance, day_of_year, albedo, array_tilt, array_azimuth, sun_zenith, sun_azimuth)
|
45
|
+
|
46
|
+
angle_of_incidence = plain_of_array_irradiance.angle_of_incidence
|
47
|
+
|
48
|
+
beam_irradiance = plain_of_array_irradiance.beam_irradiance
|
49
|
+
ground_diffuse_irradiance = plain_of_array_irradiance.ground_diffuse_irradiance
|
50
|
+
sky_diffuse_irradiance = plain_of_array_irradiance.sky_diffuse_irradiance
|
51
|
+
|
52
|
+
puts "----- Plain Of Array (POA) Irradiance -----"
|
53
|
+
puts " Angle of Incidence [º]: #{angle_of_incidence.round(4).to_s('F')}" # Slight difference from 10.8703 for 360th row in PVSC40Tutorial_Master
|
54
|
+
puts ""
|
55
|
+
puts " Beam Irradiance [W/m^2]: #{beam_irradiance.round(4).to_s('F')}" # Slight difference from 619.9822 for 360th row in PVSC40Tutorial_Master
|
56
|
+
puts " Ground Diffuse Irradiance [W/m^2]: #{ground_diffuse_irradiance.round(4).to_s('F')}" # Matches to 11.6927 for 360th row in PVSC40Tutorial_Master
|
57
|
+
puts " Sky Diffuse Irradiance [W/m^2]: #{sky_diffuse_irradiance.round(4).to_s('F')}" # Slight difference from 464.8004 for 360th row in PVSC40Tutorial_Master
|
58
|
+
puts "-------------------------------------------"
|
59
|
+
puts ""
|
60
|
+
|
61
|
+
solar_irradiance_incident_on_module_surface = beam_irradiance + ground_diffuse_irradiance + sky_diffuse_irradiance
|
62
|
+
|
63
|
+
sandia_module_data_filepath = File.join(DATA_DIRECTORY, 'sandia_module_example.csv')
|
64
|
+
sandia_pv_module = PvModule.create(sandia_module_data_filepath)
|
65
|
+
|
66
|
+
pv_temperature = PvTemperature.new(sandia_pv_module, solar_irradiance_incident_on_module_surface, reference_solar_irradiance, wind_speed, air_temperature)
|
67
|
+
|
68
|
+
puts "------------- PV temperature --------------"
|
69
|
+
puts " Estimated Cell Temperature [ºC]: #{pv_temperature.cell_temperature.round(4).to_s('F')}" # Slight difference from 47.7235 for 360th row in PVSC40Tutorial_Master
|
70
|
+
puts " Estimated Module Temperature [ºC]: #{pv_temperature.module_temperature.round(4).to_s('F')}" # Slight difference from 44.4341 for 360th row in PVSC40Tutorial_Master
|
71
|
+
puts "-------------------------------------------"
|
72
|
+
puts ""
|
73
|
+
|
74
|
+
estimated_cell_temperature = pv_temperature.cell_temperature
|
75
|
+
|
76
|
+
air_mass = AirMass.new(sun_zenith, pressure)
|
77
|
+
|
78
|
+
puts "---------------- Air Mass -----------------"
|
79
|
+
puts " Absolute Air Mass: #{air_mass.absolute_air_mass.round(4).to_s('F')}" # Slight difference from 0.8894 for 360th row in PVSC40Tutorial_Master
|
80
|
+
puts "-------------------------------------------"
|
81
|
+
puts ""
|
82
|
+
|
83
|
+
absolute_air_mass = air_mass.absolute_air_mass
|
84
|
+
|
85
|
+
pv_performance_characterization = PvPerformanceCharacterization.new(sandia_pv_module, absolute_air_mass, angle_of_incidence,
|
86
|
+
beam_irradiance, ground_diffuse_irradiance, sky_diffuse_irradiance,
|
87
|
+
estimated_cell_temperature)
|
88
|
+
|
89
|
+
puts "--- PV module IV curve characterization ---"
|
90
|
+
puts " Short Circuit Current [A]: #{pv_performance_characterization.short_circuit_current.round(4).to_s('F')}" # Slight difference from 6.4151 for 360th row in PVSC40Tutorial_Master
|
91
|
+
puts " Maximum Power Point Current [A]: #{pv_performance_characterization.maximum_power_point_current.round(4).to_s('F')}" # Matches to 5.8741 for 360th row in PVSC40Tutorial_Master
|
92
|
+
puts " Open Circuit Voltage [V]: #{pv_performance_characterization.open_circuit_voltage.round(4).to_s('F')}" # Matches to 45.2333 for 360th row in PVSC40Tutorial_Master
|
93
|
+
puts " Maximum Power Point Voltage [V]: #{pv_performance_characterization.maximum_power_point_voltage.round(4).to_s('F')}" # Matches to 36.3984 for 360th row in PVSC40Tutorial_Master
|
94
|
+
puts " Fourth Point Current [A]: #{pv_performance_characterization.fourth_point_current.round(4).to_s('F')}" # Matches to 6.3544 for 360th row in PVSC40Tutorial_Master
|
95
|
+
puts " Fifth Point Current [A]: #{pv_performance_characterization.fifth_point_current.round(4).to_s('F')}" # Slight difference from 4.3899 for 360th row in PVSC40Tutorial_Master
|
96
|
+
puts "-------------------------------------------"
|
97
|
+
puts ""
|
98
|
+
|
99
|
+
parallel_string = BigDecimal('1')
|
100
|
+
series_modules = BigDecimal('5')
|
101
|
+
array_current = pv_performance_characterization.maximum_power_point_current * parallel_string
|
102
|
+
array_voltage = pv_performance_characterization.maximum_power_point_voltage * series_modules
|
103
|
+
array_power = array_current * array_voltage
|
104
|
+
|
105
|
+
puts "------------- PV array output -------------"
|
106
|
+
puts " Array current [A]: #{array_current.round(4).to_s('F')}" # Matches to 5.8741 for 360th row in PVSC40Tutorial_Master
|
107
|
+
puts " Array voltage [V]: #{array_voltage.round(4).to_s('F')}" # Slight difference from 181.9919 for 360th row in PVSC40Tutorial_Master
|
108
|
+
puts " Array power [W]: #{array_power.round(4).to_s('F')}" # Matches to 1069.0 (with rounding) for 360th row in PVSC40Tutorial_Master
|
109
|
+
puts "-------------------------------------------"
|
110
|
+
puts ""
|
111
|
+
|
112
|
+
sandia_inverter_data_filename = File.join(DATA_DIRECTORY, 'sandia_inverter_example.csv')
|
113
|
+
sandia_inverter = Inverter.create(sandia_inverter_data_filename)
|
114
|
+
|
115
|
+
dc_to_ac_conversion = DcToAcConversion.new(sandia_inverter)
|
116
|
+
|
117
|
+
puts "--- Inverter DC to AC conversion ---"
|
118
|
+
puts " AC power [W]: #{dc_to_ac_conversion.ac_power(array_voltage, array_power).round(4).to_s('F')}" # Matches to 1016.3 (with rounding) for 360th row in PVSC40Tutorial_Master
|
119
|
+
puts "------------------------------------"
|
120
|
+
puts ""
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
|
3
|
+
# The default model for relative air mass is 'kastenyoung1989'. Different model is realized by subclass.
|
4
|
+
# 'kastenyoung1989' model is based on:
|
5
|
+
# Fritz Kasten and Andrew Young. "Revised optical air mass tables and approximation formula". Applied Optics 28:47354738
|
6
|
+
class AirMass
|
7
|
+
include CalculationHelper
|
8
|
+
|
9
|
+
def initialize(sun_zenith, pressure)
|
10
|
+
@sun_zenith = sun_zenith
|
11
|
+
@pressure = pressure
|
12
|
+
end
|
13
|
+
|
14
|
+
# Relative air mass at sea level
|
15
|
+
def relative_air_mass
|
16
|
+
relative_air_mass = BigDecimal('1') / (Math.cos(degree_to_radian(@sun_zenith).to_f) + BigDecimal('0.50572') * ((BigDecimal('6.07995') + (BigDecimal('90') - @sun_zenith)).power(-1.6364)))
|
17
|
+
relative_air_mass = BigDecimal('0') if relative_air_mass.nan?
|
18
|
+
relative_air_mass
|
19
|
+
end
|
20
|
+
|
21
|
+
# Airmass for locations not at sea level (i.e. not at standard pressure)
|
22
|
+
def absolute_air_mass
|
23
|
+
relative_air_mass * @pressure / BigDecimal('101325')
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module CalculationHelper
|
2
|
+
|
3
|
+
def degree_to_radian(degree)
|
4
|
+
degree * BigDecimal(Math::PI.to_s) / BigDecimal('180')
|
5
|
+
end
|
6
|
+
|
7
|
+
def radian_to_degree(radian)
|
8
|
+
radian * BigDecimal('180') / BigDecimal(Math::PI.to_s)
|
9
|
+
end
|
10
|
+
|
11
|
+
def bigdecimal_exp(bigdecimal_value)
|
12
|
+
BigDecimal(Math.exp(bigdecimal_value.to_f).to_s)
|
13
|
+
end
|
14
|
+
|
15
|
+
def bigdecimal_cos(angle_in_radian)
|
16
|
+
BigDecimal(Math.cos(angle_in_radian.to_f).to_s)
|
17
|
+
end
|
18
|
+
|
19
|
+
def bigdecimal_sin(angle_in_radian)
|
20
|
+
BigDecimal(Math.sin(angle_in_radian.to_f).to_s)
|
21
|
+
end
|
22
|
+
|
23
|
+
def bigdecimal_tan(angle_in_radian)
|
24
|
+
BigDecimal(Math.tan(angle_in_radian.to_f).to_s)
|
25
|
+
end
|
26
|
+
|
27
|
+
def bigdecimal_asin(sin_value)
|
28
|
+
BigDecimal(Math.asin(sin_value.to_f).to_s)
|
29
|
+
end
|
30
|
+
|
31
|
+
def bigdecimal_acos(cos_value)
|
32
|
+
BigDecimal(Math.acos(cos_value.to_f).to_s)
|
33
|
+
end
|
34
|
+
|
35
|
+
def bigdecimal_atan2(y, x)
|
36
|
+
BigDecimal(Math.atan2(y.to_f, x.to_f).to_s)
|
37
|
+
end
|
38
|
+
|
39
|
+
def bigdecimal_sqrt(bigdecimal_value)
|
40
|
+
BigDecimal(Math.sqrt(bigdecimal_value.to_f).to_s)
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
|
3
|
+
# This class uses Sandia's Grid-Connected PV Inverter model, which is based on:
|
4
|
+
# SAND2007-5036, "Performance Model for Grid-Connected Photovoltaic Inverters by D. King, S. Gonzalez, G. Galbraith, W. Boyson
|
5
|
+
# Since this paper was not available any more, we referred to https://pvpmc.sandia.gov/modeling-steps/dc-to-ac-conversion/sandia-inverter-model/
|
6
|
+
# Other inverter models are realized by subclasses.
|
7
|
+
class DcToAcConversion
|
8
|
+
|
9
|
+
def initialize(inverter)
|
10
|
+
@inverter = inverter
|
11
|
+
end
|
12
|
+
|
13
|
+
def ac_power(dc_input_voltage, dc_input_power)
|
14
|
+
# A = Pdc0 * ( 1 + C1 * (Vdc - Vdc0) )
|
15
|
+
dc_power_adjustment_parameter = @inverter.dc_power_for_maximum_ac_power_rating * (1 + @inverter.coefficient_for_dc_power_for_maximum_ac_power_rating * (dc_input_voltage - @inverter.dc_power_level_for_maximum_ac_power_rating))
|
16
|
+
|
17
|
+
# B = Ps0 * ( 1 + C2 * (Vdc - Vdc0) )
|
18
|
+
starting_dc_power_adjustment_parameter = @inverter.starting_dc_power * (1 + @inverter.coefficient_for_starting_dc_power * (dc_input_voltage - @inverter.dc_power_level_for_maximum_ac_power_rating))
|
19
|
+
|
20
|
+
# C = C0 * ( 1 + C3 * (Vdc - Vdc0) )
|
21
|
+
coefficient_adjustment_parameter = @inverter.power_adjustment_coefficient * (1 + @inverter.coefficient_for_power_adjustment_coefficient * (dc_input_voltage - @inverter.dc_power_level_for_maximum_ac_power_rating))
|
22
|
+
|
23
|
+
ac_power = (@inverter.maximum_ac_power_rating / (dc_power_adjustment_parameter - starting_dc_power_adjustment_parameter) - coefficient_adjustment_parameter * (dc_power_adjustment_parameter - starting_dc_power_adjustment_parameter)) * (dc_input_power - starting_dc_power_adjustment_parameter) +
|
24
|
+
coefficient_adjustment_parameter * (dc_input_power - starting_dc_power_adjustment_parameter).power(2)
|
25
|
+
|
26
|
+
adjust_ac_power(ac_power)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
def adjust_ac_power(ac_power)
|
31
|
+
if ac_power > @inverter.maximum_ac_power_rating # Inverter clipping at maximum rated AC Power
|
32
|
+
maximum_ac_power_rating
|
33
|
+
elsif ac_power < @inverter.starting_dc_power # Inverter night tare losses
|
34
|
+
-(@inverter.night_tare_loss.abs)
|
35
|
+
else
|
36
|
+
ac_power
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'active_model'
|
2
|
+
require 'csv'
|
3
|
+
|
4
|
+
# This is a model in model-view-controller design pattern.
|
5
|
+
# It is analogous to ActiveRecord. Hence, it's a placeholder for data from CSV file.
|
6
|
+
# Borrowed the idea from "http://kyle.conarro.com/importing-from-csv-in-rails".
|
7
|
+
class ActiveCsv
|
8
|
+
include ActiveModel::Model
|
9
|
+
|
10
|
+
def self.create(csv_filepath)
|
11
|
+
new_instance = self.new
|
12
|
+
new_instance.load_data(csv_filepath)
|
13
|
+
new_instance
|
14
|
+
end
|
15
|
+
|
16
|
+
def persisted?
|
17
|
+
false
|
18
|
+
end
|
19
|
+
|
20
|
+
def valid?
|
21
|
+
# TODO: implement this
|
22
|
+
end
|
23
|
+
|
24
|
+
def load_data(csv_filepath)
|
25
|
+
csv = CSV.new(File.new(csv_filepath), headers: true, header_converters: :symbol)
|
26
|
+
|
27
|
+
data = csv.shift
|
28
|
+
|
29
|
+
previous_header = nil;
|
30
|
+
index = 0;
|
31
|
+
|
32
|
+
data.headers.each do |header|
|
33
|
+
|
34
|
+
if "#{header}=" == "="
|
35
|
+
|
36
|
+
unless previous_header.blank?
|
37
|
+
|
38
|
+
assigned_data_for_previous_header = self.send(previous_header.to_sym)
|
39
|
+
|
40
|
+
if assigned_data_for_previous_header.instance_of? Array
|
41
|
+
assigned_data_for_previous_header << data[index]
|
42
|
+
self.send("#{previous_header}=".to_sym, assigned_data_for_previous_header)
|
43
|
+
else
|
44
|
+
self.send("#{previous_header}=".to_sym, [assigned_data_for_previous_header,data[index]])
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
else
|
50
|
+
self.send("#{header}=".to_sym, data[header])
|
51
|
+
end
|
52
|
+
|
53
|
+
previous_header = header unless header.blank?
|
54
|
+
index = index + 1;
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
|
3
|
+
# This is a model in model-view-controller design pattern.
|
4
|
+
# In this case, it is expending ActiveCsv, i.e. it is analogous to ActiveRecord. Hence, it's a placeholder for data from CSV file.
|
5
|
+
class Inverter < ActiveCsv
|
6
|
+
|
7
|
+
attr_accessor :name, :vac, :pac0, :pdc0, :vdc0, :ps0, :c0, :c1, :c2, :c3
|
8
|
+
attr_accessor :pnt, :vdcmax, :idcmax, :mpptlow, :mppthi, :librarytype, :libraryname
|
9
|
+
|
10
|
+
def maximum_ac_power_rating
|
11
|
+
BigDecimal(self.pac0.to_s)
|
12
|
+
end
|
13
|
+
|
14
|
+
def dc_power_for_maximum_ac_power_rating
|
15
|
+
BigDecimal(self.pdc0.to_s)
|
16
|
+
end
|
17
|
+
|
18
|
+
def coefficient_for_dc_power_for_maximum_ac_power_rating
|
19
|
+
BigDecimal(self.c1.to_s)
|
20
|
+
end
|
21
|
+
|
22
|
+
def dc_power_level_for_maximum_ac_power_rating
|
23
|
+
BigDecimal(self.vdc0.to_s)
|
24
|
+
end
|
25
|
+
|
26
|
+
def starting_dc_power
|
27
|
+
BigDecimal(self.ps0.to_s)
|
28
|
+
end
|
29
|
+
|
30
|
+
def coefficient_for_starting_dc_power
|
31
|
+
BigDecimal(self.c2.to_s)
|
32
|
+
end
|
33
|
+
|
34
|
+
def power_adjustment_coefficient
|
35
|
+
BigDecimal(self.c0.to_s)
|
36
|
+
end
|
37
|
+
|
38
|
+
def coefficient_for_power_adjustment_coefficient
|
39
|
+
BigDecimal(self.c3.to_s)
|
40
|
+
end
|
41
|
+
|
42
|
+
def night_tare_loss
|
43
|
+
BigDecimal(self.pnt.to_s)
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
|
3
|
+
# This is a model in model-view-controller design pattern.
|
4
|
+
# In this case, it is expending ActiveCsv, i.e. it is analogous to ActiveRecord. Hence, it's a placeholder for data from CSV file.
|
5
|
+
class PvModule < ActiveCsv
|
6
|
+
|
7
|
+
attr_accessor :name, :vintage, :material, :area, :alphaisc, :alphaimp, :isc0, :imp0, :voc0, :vmp0
|
8
|
+
attr_accessor :betavoc, :betavmp, :mbetavoc, :mbetavmp, :ns, :np, :delt, :fd, :n, :ix0, :ixx0, :a_wind, :b_wind
|
9
|
+
attr_accessor :c, :a, :b
|
10
|
+
|
11
|
+
def short_circuit_current_0
|
12
|
+
BigDecimal(self.isc0.to_s)
|
13
|
+
end
|
14
|
+
|
15
|
+
def air_mass_variation_polynomial_coefficients
|
16
|
+
convert_coefficients_to_bigdecimal(self.a)
|
17
|
+
end
|
18
|
+
|
19
|
+
def angle_of_incidence_polynominal_coefficients
|
20
|
+
convert_coefficients_to_bigdecimal(self.b)
|
21
|
+
end
|
22
|
+
|
23
|
+
def diffuse_irradiance_factor
|
24
|
+
BigDecimal(self.fd)
|
25
|
+
end
|
26
|
+
|
27
|
+
def normalized_temperature_coefficient_for_short_circuit_current
|
28
|
+
BigDecimal(self.alphaisc.to_s)
|
29
|
+
end
|
30
|
+
|
31
|
+
def maximum_power_point_current_0
|
32
|
+
BigDecimal(self.imp0.to_s)
|
33
|
+
end
|
34
|
+
|
35
|
+
def performance_coefficients
|
36
|
+
convert_coefficients_to_bigdecimal(self.c)
|
37
|
+
end
|
38
|
+
|
39
|
+
def normalized_temperature_coefficient_for_maximum_power_point_current
|
40
|
+
BigDecimal(self.alphaimp.to_s)
|
41
|
+
end
|
42
|
+
|
43
|
+
def open_circuit_voltage_0
|
44
|
+
BigDecimal(self.voc0.to_s)
|
45
|
+
end
|
46
|
+
|
47
|
+
def number_of_cells_in_series
|
48
|
+
BigDecimal(self.ns.to_s)
|
49
|
+
end
|
50
|
+
|
51
|
+
def diode_factor
|
52
|
+
BigDecimal(self.n.to_s)
|
53
|
+
end
|
54
|
+
|
55
|
+
def temperature_coefficient_for_open_circuit_voltage
|
56
|
+
BigDecimal(self.betavoc.to_s)
|
57
|
+
end
|
58
|
+
|
59
|
+
def irradiance_dependent_temperature_coefficient_for_open_circuit_voltage
|
60
|
+
BigDecimal(self.mbetavoc.to_s)
|
61
|
+
end
|
62
|
+
|
63
|
+
def maximum_power_point_voltage_0
|
64
|
+
BigDecimal(self.vmp0.to_s)
|
65
|
+
end
|
66
|
+
|
67
|
+
def temperature_coefficient_for_maximum_power_point_voltage
|
68
|
+
BigDecimal(self.betavmp.to_s)
|
69
|
+
end
|
70
|
+
|
71
|
+
def irradiance_dependent_temperature_coefficient_for_maximum_power_point_voltage
|
72
|
+
BigDecimal(self.mbetavmp.to_s)
|
73
|
+
end
|
74
|
+
|
75
|
+
def fourth_point_current_0
|
76
|
+
BigDecimal(self.ix0.to_s)
|
77
|
+
end
|
78
|
+
|
79
|
+
def fifth_point_current_0
|
80
|
+
BigDecimal(self.ixx0.to_s)
|
81
|
+
end
|
82
|
+
|
83
|
+
def temperature_upper_limit_coefficient
|
84
|
+
BigDecimal(self.a_wind.to_s)
|
85
|
+
end
|
86
|
+
|
87
|
+
def wind_speed_decrease_rate_coefficient
|
88
|
+
BigDecimal(self.b_wind.to_s)
|
89
|
+
end
|
90
|
+
|
91
|
+
def cell_module_back_surface_temperature_difference
|
92
|
+
BigDecimal(self.delt.to_s)
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
def convert_coefficients_to_bigdecimal(coefficients)
|
97
|
+
coefficients.map {|coefficient| BigDecimal(coefficient.to_s)}.compact
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
|
3
|
+
# For Sky Diffuse Irradiance, "Reindl's 1990 model" is used.
|
4
|
+
# Other models are realized by subclasses.
|
5
|
+
#
|
6
|
+
# References
|
7
|
+
# [1] Loutzenhiser P.G. et. al. "Empirical validation of models to compute
|
8
|
+
# solar irradiance on inclined surfaces for building energy simulation"
|
9
|
+
# 2007, Solar Energy vol. 81. pp. 254-267
|
10
|
+
# [2] Reindl, D.T., Beckmann, W.A., Duffie, J.A., 1990a. Diffuse fraction
|
11
|
+
# correlations. Solar Energy 45 (1), 17.
|
12
|
+
# [3] Reindl, D.T., Beckmann, W.A., Duffie, J.A., 1990b. Evaluation of hourly
|
13
|
+
# tilted surface radiation models. Solar Energy 45 (1), 917.
|
14
|
+
# [4] Solar radiation basics http://solardat.uoregon.edu/SolarRadiationBasics.html
|
15
|
+
#
|
16
|
+
# TODO: Consider to pass necessary parameters to the respective methods instead of passing all of them to the constructor.
|
17
|
+
# Haven't decided which way would be better.
|
18
|
+
class PlainOfArrayIrradiance
|
19
|
+
include CalculationHelper
|
20
|
+
|
21
|
+
AVEARGE_EXTRATERRESTRIAL_IRRADIANCE = BigDecimal('1367') # [W/m^2]
|
22
|
+
SMALL_VALUE_FOR_SKY_DIFFUSE_IRRADIANCE = BigDecimal('0.000001') # In order to make the calculated value consistent with the one by PVLIB_MatLab
|
23
|
+
|
24
|
+
def initialize(direct_normal_irradiance, global_horizontal_irradiance, diffuse_horizontal_irradiance, day_of_year, albedo, surface_tilt, surface_azimuth, sun_zenith, sun_azimuth)
|
25
|
+
@direct_normal_irradiance = direct_normal_irradiance # DNI [W/m^2]
|
26
|
+
@global_horizontal_irradiance = global_horizontal_irradiance # GHI [W/m^2]
|
27
|
+
@diffuse_horizontal_irradiance = diffuse_horizontal_irradiance # DHI [W/m^2]
|
28
|
+
@day_of_year = day_of_year
|
29
|
+
@albedo = albedo
|
30
|
+
@surface_tilt = surface_tilt
|
31
|
+
@surface_azimuth = surface_azimuth
|
32
|
+
@sun_zenith = sun_zenith
|
33
|
+
@sun_azimuth = sun_azimuth
|
34
|
+
end
|
35
|
+
|
36
|
+
def beam_irradiance
|
37
|
+
@direct_normal_irradiance * bigdecimal_cos(degree_to_radian(angle_of_incidence))
|
38
|
+
end
|
39
|
+
|
40
|
+
def ground_diffuse_irradiance
|
41
|
+
@global_horizontal_irradiance * @albedo * (1 - bigdecimal_cos(degree_to_radian(@surface_tilt))) * BigDecimal('0.5')
|
42
|
+
end
|
43
|
+
|
44
|
+
# Based on the equation (5) of [3]
|
45
|
+
# The comment in PVLIB_MatLab says it's based on the modified version of the equation (8) of [1].
|
46
|
+
# But I observe that the code is exactly same as the equation (5) of [3].
|
47
|
+
def sky_diffuse_irradiance
|
48
|
+
@diffuse_horizontal_irradiance * (anisotropy_index * geometric_factor +
|
49
|
+
(1 - anisotropy_index) * ((BigDecimal('1') + bigdecimal_cos(degree_to_radian(@surface_tilt))) / BigDecimal('2')) * anisotropic_correction_factor)
|
50
|
+
end
|
51
|
+
|
52
|
+
# This is used only for calculating Sky Diffuse Irradiance.
|
53
|
+
# But it's made public method just in case some other code needs it.
|
54
|
+
# Reference: [4]
|
55
|
+
def extraterrestrial_irradiance
|
56
|
+
earth_orbit_angle_in_radian = BigDecimal('2') * BigDecimal(Math::PI.to_s) * @day_of_year / BigDecimal('365')
|
57
|
+
sun_earth_distance_factor_square = BigDecimal('1.00011') + BigDecimal('0.034221') * bigdecimal_cos(earth_orbit_angle_in_radian) + BigDecimal('0.001280') * bigdecimal_sin(earth_orbit_angle_in_radian) +
|
58
|
+
BigDecimal('0.000719') * bigdecimal_cos(BigDecimal('2') * earth_orbit_angle_in_radian) + BigDecimal('0.000077') * bigdecimal_sin(BigDecimal('2') * earth_orbit_angle_in_radian)
|
59
|
+
AVEARGE_EXTRATERRESTRIAL_IRRADIANCE * sun_earth_distance_factor_square
|
60
|
+
end
|
61
|
+
|
62
|
+
def angle_of_incidence
|
63
|
+
cosine_of_angle_of_incidence = bigdecimal_cos(degree_to_radian(@sun_zenith)) * bigdecimal_cos(degree_to_radian(@surface_tilt)) +
|
64
|
+
bigdecimal_sin(degree_to_radian(@surface_tilt)) * bigdecimal_sin(degree_to_radian(@sun_zenith)) * bigdecimal_cos(degree_to_radian(@sun_azimuth - @surface_azimuth))
|
65
|
+
|
66
|
+
if cosine_of_angle_of_incidence > BigDecimal('1')
|
67
|
+
cosine_of_angle_of_incidence = BigDecimal('1')
|
68
|
+
elsif cosine_of_angle_of_incidence < BigDecimal('-1')
|
69
|
+
cosine_of_angle_of_incidence = BigDecimal('-1')
|
70
|
+
end
|
71
|
+
|
72
|
+
radian_to_degree(bigdecimal_acos(cosine_of_angle_of_incidence))
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
# Rb
|
77
|
+
# Reference:
|
78
|
+
# 3. Titled Surface Models
|
79
|
+
# in [3]
|
80
|
+
def geometric_factor
|
81
|
+
hourly_beam_radiation_on_tilted_surface / hourly_beam_radiation_on_horizontal_surface
|
82
|
+
end
|
83
|
+
|
84
|
+
# I[b,T]
|
85
|
+
def hourly_beam_radiation_on_tilted_surface
|
86
|
+
hourly_bean_radiation = bigdecimal_cos(degree_to_radian(@surface_tilt)) * bigdecimal_cos(degree_to_radian(@sun_zenith)) +
|
87
|
+
bigdecimal_sin(degree_to_radian(@surface_tilt)) * bigdecimal_sin(degree_to_radian(@sun_zenith)) * bigdecimal_cos(degree_to_radian(@sun_azimuth - @surface_azimuth))
|
88
|
+
[hourly_bean_radiation, BigDecimal('0')].max
|
89
|
+
end
|
90
|
+
|
91
|
+
# Ib
|
92
|
+
def hourly_beam_radiation_on_horizontal_surface
|
93
|
+
[bigdecimal_cos(degree_to_radian(@sun_zenith)), BigDecimal('0.01745')].max
|
94
|
+
end
|
95
|
+
|
96
|
+
# AI
|
97
|
+
# "defines the portion of the diffuse radiation to be treated as circumsolar with the remaining portion considered isotropic"[3]
|
98
|
+
def anisotropy_index
|
99
|
+
@direct_normal_irradiance / extraterrestrial_irradiance
|
100
|
+
end
|
101
|
+
|
102
|
+
def anisotropic_correction_factor
|
103
|
+
sin_cube_of_half_surface_tilt = (bigdecimal_sin(degree_to_radian(@surface_tilt/BigDecimal('2')))).power(3)
|
104
|
+
BigDecimal('1') + horizontal_brighting_correction_factor * sin_cube_of_half_surface_tilt
|
105
|
+
end
|
106
|
+
|
107
|
+
# f: Horizontal brighting correction factor
|
108
|
+
# page 13 of [3]
|
109
|
+
def horizontal_brighting_correction_factor
|
110
|
+
adjusted_global_horizontal_irradiance = @global_horizontal_irradiance < SMALL_VALUE_FOR_SKY_DIFFUSE_IRRADIANCE ? SMALL_VALUE_FOR_SKY_DIFFUSE_IRRADIANCE : @global_horizontal_irradiance
|
111
|
+
# In PVLIB_MatLab as well as PVLIB_Python, there is a following adjustment.
|
112
|
+
# However, all the negative numbers are already converted to SMALL_VALUE_FOR_SKY_DIFFUSE_IRRADIANCE in the above statement,
|
113
|
+
# the following statement doesn't do anything.
|
114
|
+
# I'm just writing it to remind that it is there in PVLIB_MatLab and PVLIB_Python.
|
115
|
+
# adjusted_global_horizontal_irradiance = @global_horizontal_irradiance < BigDecimal('0') ? BigDecimal('0') : @global_horizontal_irradiance
|
116
|
+
bigdecimal_sqrt(horizontal_beam_irradiance / adjusted_global_horizontal_irradiance)
|
117
|
+
end
|
118
|
+
|
119
|
+
# HB
|
120
|
+
def horizontal_beam_irradiance
|
121
|
+
calculated_horizontal_beam_irradiance = @direct_normal_irradiance * bigdecimal_cos(degree_to_radian(@sun_zenith))
|
122
|
+
calculated_horizontal_beam_irradiance < BigDecimal('0') ? BigDecimal('0') : calculated_horizontal_beam_irradiance
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
require 'bigdecimal/math'
|
3
|
+
|
4
|
+
# This class uses Sandia PV Array Performance Model, which is based on:
|
5
|
+
# King, D. et al, 2004, "Sandia Photovoltaic Array Performance Model", SAND Report 3535, Sandia National Laboratories, Albuquerque, NM
|
6
|
+
# Other models are realized by subclasses.
|
7
|
+
class PvPerformanceCharacterization
|
8
|
+
|
9
|
+
REFERECE_SOLAR_IRRADIANCE = BigDecimal('1000') # E0 [W/m^2]
|
10
|
+
REFERECE_CELL_TEMPERATURE = BigDecimal('25') # T0 [ºC]
|
11
|
+
BOLTZMANN_CONSTANT = BigDecimal('1.38066E-23') # k [J/K]
|
12
|
+
ELEMENTARY_CHARGE = BigDecimal('1.60218E-19') # q [C(coulomb)]
|
13
|
+
|
14
|
+
def initialize(pv_module, absolute_air_mass, angle_of_incidence, beam_irradiance, ground_irradiance, sky_diffuse_irradiance, estimated_cell_temperature)
|
15
|
+
@pv_module = pv_module
|
16
|
+
@effective_solar_irradiance = empirical_effective_solar_irradiance(absolute_air_mass, angle_of_incidence, beam_irradiance, ground_irradiance, sky_diffuse_irradiance)
|
17
|
+
@estimated_cell_temperature = estimated_cell_temperature
|
18
|
+
end
|
19
|
+
|
20
|
+
# Isc
|
21
|
+
def short_circuit_current
|
22
|
+
@pv_module.short_circuit_current_0 * @effective_solar_irradiance * (1 + @pv_module.normalized_temperature_coefficient_for_short_circuit_current * (@estimated_cell_temperature - REFERECE_CELL_TEMPERATURE))
|
23
|
+
end
|
24
|
+
|
25
|
+
alias :isc :short_circuit_current
|
26
|
+
|
27
|
+
# Imp
|
28
|
+
def maximum_power_point_current
|
29
|
+
@pv_module.maximum_power_point_current_0 * (@pv_module.performance_coefficients[0] * @effective_solar_irradiance + @pv_module.performance_coefficients[1] * @effective_solar_irradiance.power(2)) *
|
30
|
+
(1 + @pv_module.normalized_temperature_coefficient_for_maximum_power_point_current * (@estimated_cell_temperature - REFERECE_CELL_TEMPERATURE))
|
31
|
+
end
|
32
|
+
|
33
|
+
alias :imp :maximum_power_point_current
|
34
|
+
|
35
|
+
# Voc
|
36
|
+
def open_circuit_voltage
|
37
|
+
@pv_module.open_circuit_voltage_0 + @pv_module.number_of_cells_in_series * thermal_voltage_per_cell * BigMath.log(@effective_solar_irradiance, 4) +
|
38
|
+
temperature_coefficient_for_open_circuit_voltage * (@estimated_cell_temperature - REFERECE_CELL_TEMPERATURE)
|
39
|
+
end
|
40
|
+
|
41
|
+
alias :voc :open_circuit_voltage
|
42
|
+
|
43
|
+
# Vmp
|
44
|
+
def maximum_power_point_voltage
|
45
|
+
@pv_module.maximum_power_point_voltage_0 + @pv_module.performance_coefficients[2] * @pv_module.number_of_cells_in_series * thermal_voltage_per_cell * BigMath.log(@effective_solar_irradiance, 4) +
|
46
|
+
@pv_module.performance_coefficients[3] * @pv_module.number_of_cells_in_series * (thermal_voltage_per_cell * BigMath.log(@effective_solar_irradiance, 4)).power(2) +
|
47
|
+
temperature_coefficient_for_maximum_power_point_voltage * (@estimated_cell_temperature - REFERECE_CELL_TEMPERATURE)
|
48
|
+
end
|
49
|
+
|
50
|
+
alias :vmp :maximum_power_point_voltage
|
51
|
+
|
52
|
+
# Ix
|
53
|
+
def fourth_point_current
|
54
|
+
@pv_module.fourth_point_current_0 * (@pv_module.performance_coefficients[4] * @effective_solar_irradiance + @pv_module.performance_coefficients[5] * @effective_solar_irradiance.power(2)) *
|
55
|
+
(1 + @pv_module.normalized_temperature_coefficient_for_short_circuit_current * (@estimated_cell_temperature - REFERECE_CELL_TEMPERATURE))
|
56
|
+
end
|
57
|
+
|
58
|
+
alias :ix :fourth_point_current
|
59
|
+
|
60
|
+
# Ixx
|
61
|
+
def fifth_point_current
|
62
|
+
@pv_module.fifth_point_current_0 * (@pv_module.performance_coefficients[6] * @effective_solar_irradiance + @pv_module.performance_coefficients[7] * @effective_solar_irradiance.power(2)) *
|
63
|
+
(1 + @pv_module.normalized_temperature_coefficient_for_maximum_power_point_current * (@estimated_cell_temperature - REFERECE_CELL_TEMPERATURE))
|
64
|
+
end
|
65
|
+
|
66
|
+
alias :ixx :fifth_point_current
|
67
|
+
|
68
|
+
private
|
69
|
+
# Ee
|
70
|
+
def empirical_effective_solar_irradiance(absolute_air_mass, angle_of_incidence, beam_irradiance, ground_irradiance, sky_diffuse_irradiance)
|
71
|
+
diffuse_irradiance = ground_irradiance + sky_diffuse_irradiance
|
72
|
+
|
73
|
+
empirical_solar_spectral_influence_on_short_circuit_current(absolute_air_mass) *
|
74
|
+
((beam_irradiance * empirical_opitical_influence_on_short_circuit_current(angle_of_incidence) + @pv_module.diffuse_irradiance_factor * diffuse_irradiance)/REFERECE_SOLAR_IRRADIANCE)
|
75
|
+
end
|
76
|
+
|
77
|
+
# f1(AMa)
|
78
|
+
def empirical_solar_spectral_influence_on_short_circuit_current(absolute_air_mass)
|
79
|
+
@pv_module.air_mass_variation_polynomial_coefficients[4] +
|
80
|
+
@pv_module.air_mass_variation_polynomial_coefficients[3] * absolute_air_mass +
|
81
|
+
@pv_module.air_mass_variation_polynomial_coefficients[2] * absolute_air_mass.power(2) +
|
82
|
+
@pv_module.air_mass_variation_polynomial_coefficients[1] * absolute_air_mass.power(3) +
|
83
|
+
@pv_module.air_mass_variation_polynomial_coefficients[0] * absolute_air_mass.power(4)
|
84
|
+
end
|
85
|
+
|
86
|
+
# f2(AOI)
|
87
|
+
def empirical_opitical_influence_on_short_circuit_current(angle_of_incidence)
|
88
|
+
@pv_module.angle_of_incidence_polynominal_coefficients[5] +
|
89
|
+
@pv_module.angle_of_incidence_polynominal_coefficients[4] * angle_of_incidence +
|
90
|
+
@pv_module.angle_of_incidence_polynominal_coefficients[3] * angle_of_incidence.power(2) +
|
91
|
+
@pv_module.angle_of_incidence_polynominal_coefficients[2] * angle_of_incidence.power(3) +
|
92
|
+
@pv_module.angle_of_incidence_polynominal_coefficients[1] * angle_of_incidence.power(4) +
|
93
|
+
@pv_module.angle_of_incidence_polynominal_coefficients[0] * angle_of_incidence.power(5)
|
94
|
+
end
|
95
|
+
|
96
|
+
# delta(Tc)
|
97
|
+
def thermal_voltage_per_cell
|
98
|
+
@pv_module.diode_factor * BOLTZMANN_CONSTANT * (@estimated_cell_temperature + BigDecimal('273.15')) / ELEMENTARY_CHARGE
|
99
|
+
end
|
100
|
+
|
101
|
+
# BetaVoc
|
102
|
+
def temperature_coefficient_for_open_circuit_voltage
|
103
|
+
@pv_module.temperature_coefficient_for_open_circuit_voltage + @pv_module.irradiance_dependent_temperature_coefficient_for_open_circuit_voltage * (1-@effective_solar_irradiance)
|
104
|
+
end
|
105
|
+
|
106
|
+
# BetaVmp
|
107
|
+
def temperature_coefficient_for_maximum_power_point_voltage
|
108
|
+
@pv_module.temperature_coefficient_for_maximum_power_point_voltage + @pv_module.irradiance_dependent_temperature_coefficient_for_maximum_power_point_voltage * (1-@effective_solar_irradiance)
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
|
3
|
+
# This class calculates cell and module temperatures using Sandia PV Array Performance Model, which is based on:
|
4
|
+
# King, D. et al, 2004, "Sandia Photovoltaic Array Performance Model", SAND Report 3535, Sandia National Laboratories, Albuquerque, NM
|
5
|
+
class PvTemperature
|
6
|
+
include CalculationHelper
|
7
|
+
|
8
|
+
def initialize(pv_module, solar_irradiance_incident_on_module_surface, reference_solar_irradiance, wind_speed, air_temperature)
|
9
|
+
@pv_module = pv_module
|
10
|
+
@solar_irradiance_incident_on_module_surface = solar_irradiance_incident_on_module_surface
|
11
|
+
@reference_solar_irradiance = reference_solar_irradiance
|
12
|
+
@wind_speed = wind_speed
|
13
|
+
@air_temperature = air_temperature
|
14
|
+
end
|
15
|
+
|
16
|
+
def cell_temperature
|
17
|
+
module_temperature +
|
18
|
+
(@solar_irradiance_incident_on_module_surface / @reference_solar_irradiance) * @pv_module.cell_module_back_surface_temperature_difference
|
19
|
+
end
|
20
|
+
|
21
|
+
def module_temperature
|
22
|
+
back_surface_module_temperature = @solar_irradiance_incident_on_module_surface * bigdecimal_exp(@pv_module.temperature_upper_limit_coefficient + @pv_module.wind_speed_decrease_rate_coefficient * @wind_speed) +
|
23
|
+
@air_temperature
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
|
3
|
+
class SolarEphemeris
|
4
|
+
include CalculationHelper
|
5
|
+
|
6
|
+
def initialize(time, location, pressure: BigDecimal('101325'), temperature: BigDecimal('12'))
|
7
|
+
@time = time
|
8
|
+
@location = location
|
9
|
+
@pressure = pressure
|
10
|
+
@temperature = temperature
|
11
|
+
|
12
|
+
utc_offset_in_hours = BigDecimal(@time.utc_offset.to_s) / BigDecimal('3600')
|
13
|
+
dec_hours = BigDecimal(@time.hour.to_s) + BigDecimal(@time.min.to_s) / BigDecimal('60') + BigDecimal(@time.sec.to_s) / BigDecimal('3600')
|
14
|
+
@universal_date = BigDecimal(@time.yday.to_s) + ((dec_hours - utc_offset_in_hours) / BigDecimal('24')).floor
|
15
|
+
@universal_hour = (dec_hours - utc_offset_in_hours).modulo(BigDecimal('24'))
|
16
|
+
end
|
17
|
+
|
18
|
+
def sun_azimuth
|
19
|
+
calculated_sun_azimuth = radian_to_degree(bigdecimal_atan2(BigDecimal('-1') * bigdecimal_sin(degree_to_radian(hour_angle)),
|
20
|
+
bigdecimal_cos(degree_to_radian(@location.latitude)) * bigdecimal_tan(degree_to_radian(declination)) - bigdecimal_sin(degree_to_radian(@location.latitude)) * bigdecimal_cos(degree_to_radian(hour_angle))))
|
21
|
+
sun_azimuth = calculated_sun_azimuth < BigDecimal('0') ? calculated_sun_azimuth + BigDecimal('360') : calculated_sun_azimuth
|
22
|
+
sun_azimuth
|
23
|
+
end
|
24
|
+
|
25
|
+
def sun_elevation
|
26
|
+
# Caching because it's used several times for the calculation of apparent_sun_elevation
|
27
|
+
@sun_elevation ||= radian_to_degree(bigdecimal_asin(bigdecimal_cos(degree_to_radian(@location.latitude)) * bigdecimal_cos(degree_to_radian(declination)) * bigdecimal_cos(degree_to_radian(hour_angle)) +
|
28
|
+
bigdecimal_sin(degree_to_radian(@location.latitude)) * bigdecimal_sin(degree_to_radian(declination))))
|
29
|
+
end
|
30
|
+
|
31
|
+
def apparent_sun_elevation
|
32
|
+
sun_elevation + reflaction
|
33
|
+
end
|
34
|
+
|
35
|
+
def solar_time
|
36
|
+
# BigDecimal#sign returns 2 for positive value, -2 for negative value, 1 for 0 and -1 for -0.
|
37
|
+
# Hence, decided to use the following statement:
|
38
|
+
hour_angle_sign = hour_angle < BigDecimal('0') ? BigDecimal('-1') : BigDecimal('1')
|
39
|
+
|
40
|
+
adjusted_hour_angle = hour_angle.abs > BigDecimal('180') ? hour_angle - (BigDecimal('360') * hour_angle_sign) : hour_angle
|
41
|
+
|
42
|
+
(BigDecimal('180') + adjusted_hour_angle) / BigDecimal('15')
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
def hour_angle
|
47
|
+
loc_ast - rt_ascen
|
48
|
+
end
|
49
|
+
|
50
|
+
def declination
|
51
|
+
radian_to_degree(bigdecimal_asin(bigdecimal_sin(degree_to_radian(obliquity)) * bigdecimal_sin(degree_to_radian(ec_lon))))
|
52
|
+
end
|
53
|
+
|
54
|
+
# For hour_angle calculation
|
55
|
+
# LocAST in PVLIB_MatLab. Couldn't find what it stands for.
|
56
|
+
# Also for other variables names, couldn't find what they stand for.
|
57
|
+
def loc_ast
|
58
|
+
t = e_zero / 36525
|
59
|
+
|
60
|
+
gmst0 = BigDecimal('6') / BigDecimal('24') + BigDecimal('38') / BigDecimal('1440') +
|
61
|
+
(BigDecimal('45.836') + BigDecimal('8640184.542') * t + BigDecimal('0.0929') * t.power(2) ) / BigDecimal('86400')
|
62
|
+
gmst0 = BigDecimal('360') * (gmst0 - gmst0.floor)
|
63
|
+
gmsti = (gmst0 + BigDecimal('360') * (BigDecimal('1.0027379093') * @universal_hour / BigDecimal('24'))).modulo(BigDecimal('360'))
|
64
|
+
|
65
|
+
(BigDecimal('360') + gmsti + @location.longitude).modulo(BigDecimal('360'))
|
66
|
+
end
|
67
|
+
|
68
|
+
# For hour_angle calculation
|
69
|
+
# RtAscen in PVLIB_MatLab. Couldn't find what it stands for.
|
70
|
+
# Also for other variables names, couldn't find what they stand for.
|
71
|
+
def rt_ascen
|
72
|
+
radian_to_degree(bigdecimal_atan2(bigdecimal_cos(degree_to_radian(obliquity)) * bigdecimal_sin(degree_to_radian(ec_lon)), bigdecimal_cos(degree_to_radian(ec_lon))))
|
73
|
+
end
|
74
|
+
|
75
|
+
def obliquity
|
76
|
+
BigDecimal('23.452294') - BigDecimal('0.0130125') * t1 - BigDecimal('0.00000164') * t1.power(2) + BigDecimal('0.000000503') * t1.power(3)
|
77
|
+
end
|
78
|
+
|
79
|
+
def ec_lon
|
80
|
+
(ml_perigee + true_anom).modulo(BigDecimal('360')) - abber
|
81
|
+
end
|
82
|
+
|
83
|
+
def ml_perigee
|
84
|
+
BigDecimal('281.22083') + BigDecimal('0.0000470684') * epoch_date + BigDecimal('0.000453') * t1.power(2) + BigDecimal('0.000003') * t1.power(3)
|
85
|
+
end
|
86
|
+
|
87
|
+
def true_anom
|
88
|
+
# Cannot find a proper name for these values:
|
89
|
+
temp1 = ((BigDecimal('1') + eccen) / (BigDecimal('1') - eccen)).power(0.5) * bigdecimal_tan(degree_to_radian(eccen_anom / BigDecimal('2')))
|
90
|
+
temp2 = radian_to_degree(bigdecimal_atan2(temp1, BigDecimal('1')))
|
91
|
+
BigDecimal('2') * temp2.modulo(BigDecimal('360'))
|
92
|
+
end
|
93
|
+
|
94
|
+
def abber
|
95
|
+
BigDecimal('20') / BigDecimal('3600')
|
96
|
+
end
|
97
|
+
|
98
|
+
def eccen
|
99
|
+
BigDecimal('0.01675104') - BigDecimal('0.0000418') * t1 - BigDecimal('0.000000126') * t1.power(2)
|
100
|
+
end
|
101
|
+
|
102
|
+
def eccen_anom
|
103
|
+
mean_anom = (BigDecimal('358.47583') + BigDecimal('0.985600267') * epoch_date - BigDecimal('0.00015') * t1.power(2) - BigDecimal('0.000003') * t1.power(3)).modulo(BigDecimal('360'))
|
104
|
+
|
105
|
+
eccen_anom = mean_anom
|
106
|
+
e = BigDecimal('0')
|
107
|
+
while (mean_anom - e).abs > BigDecimal('0.0001')
|
108
|
+
e = eccen_anom
|
109
|
+
eccen_anom = mean_anom + radian_to_degree(eccen) * bigdecimal_sin(degree_to_radian(e))
|
110
|
+
end
|
111
|
+
eccen_anom
|
112
|
+
end
|
113
|
+
|
114
|
+
def t1
|
115
|
+
epoch_date / BigDecimal('36525')
|
116
|
+
end
|
117
|
+
|
118
|
+
def epoch_date
|
119
|
+
e_zero + @universal_hour / BigDecimal('24')
|
120
|
+
end
|
121
|
+
|
122
|
+
def e_zero
|
123
|
+
year_starting_from_1900 = BigDecimal(@time.year.to_s) - BigDecimal('1900')
|
124
|
+
year_begin = BigDecimal('365') * year_starting_from_1900 + ((year_starting_from_1900 - BigDecimal('1')) / BigDecimal('4')).floor - BigDecimal('0.5')
|
125
|
+
year_begin + @universal_date
|
126
|
+
end
|
127
|
+
|
128
|
+
# For apparent_sun_elevation calculation
|
129
|
+
def reflaction
|
130
|
+
tangent_of_elevation = bigdecimal_tan(degree_to_radian(sun_elevation))
|
131
|
+
|
132
|
+
raw_reflaction = if sun_elevation > BigDecimal('5') && sun_elevation <= BigDecimal('85')
|
133
|
+
BigDecimal('58.1') / tangent_of_elevation - BigDecimal('0.07') / (tangent_of_elevation.power(3)) + BigDecimal('.000086') / (tangent_of_elevation.power(5))
|
134
|
+
elsif sun_elevation > BigDecimal('-0.575') && sun_elevation <= BigDecimal('5')
|
135
|
+
sun_elevation * (BigDecimal('-518.2') + sun_elevation * (BigDecimal('103.4') + sun_elevation * (BigDecimal('-12.79') + sun_elevation * BigDecimal('0.711')))) + BigDecimal('1735')
|
136
|
+
elsif sun_elevation > BigDecimal('-1') && sun_elevation <= BigDecimal('-0.575')
|
137
|
+
BigDecimal('-20.774') / tangent_of_elevation
|
138
|
+
else
|
139
|
+
BigDecimal('0')
|
140
|
+
end
|
141
|
+
|
142
|
+
raw_reflaction * (BigDecimal('283') / (BigDecimal('273') + @temperature)) * @pressure / BigDecimal('101325') / BigDecimal('3600')
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
data/lib/PVLIB_Ruby.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'pry'
|
2
|
+
require 'pvlib_ruby/version'
|
3
|
+
require 'pvlib_ruby/models/active_csv'
|
4
|
+
require 'pvlib_ruby/models/pv_module'
|
5
|
+
require 'pvlib_ruby/models/inverter'
|
6
|
+
require 'pvlib_ruby/pv_performance_characterization'
|
7
|
+
require 'pvlib_ruby/dc_to_ac_conversion'
|
8
|
+
require 'pvlib_ruby/calculation_helper'
|
9
|
+
require 'pvlib_ruby/air_mass'
|
10
|
+
require 'pvlib_ruby/pv_temperature'
|
11
|
+
require 'pvlib_ruby/plain_of_array_irradiance'
|
12
|
+
require 'pvlib_ruby/models/location'
|
13
|
+
require 'pvlib_ruby/solar_ephemeris'
|
14
|
+
|
15
|
+
module PvlibRuby
|
16
|
+
# Your code goes here...
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: PVLIB_Ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tadatoshi Takahashi
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-06-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activemodel
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.9'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.9'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: PVLIB_MatLab translated to Ruby code in Object-Oriented way.
|
70
|
+
email:
|
71
|
+
- tadatoshi@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- ".gitignore"
|
77
|
+
- ".rspec"
|
78
|
+
- ".ruby-gemset"
|
79
|
+
- ".ruby-version"
|
80
|
+
- ".travis.yml"
|
81
|
+
- CODE_OF_CONDUCT.md
|
82
|
+
- Gemfile
|
83
|
+
- LICENSE.txt
|
84
|
+
- PVLIB_Ruby.gemspec
|
85
|
+
- README.md
|
86
|
+
- Rakefile
|
87
|
+
- bin/console
|
88
|
+
- bin/setup
|
89
|
+
- examples/data/sandia_inverter_example.csv
|
90
|
+
- examples/data/sandia_module_example.csv
|
91
|
+
- examples/pv_power_generation_example.rb
|
92
|
+
- lib/PVLIB_Ruby.rb
|
93
|
+
- lib/PVLIB_Ruby/air_mass.rb
|
94
|
+
- lib/PVLIB_Ruby/calculation_helper.rb
|
95
|
+
- lib/PVLIB_Ruby/dc_to_ac_conversion.rb
|
96
|
+
- lib/PVLIB_Ruby/models/active_csv.rb
|
97
|
+
- lib/PVLIB_Ruby/models/inverter.rb
|
98
|
+
- lib/PVLIB_Ruby/models/location.rb
|
99
|
+
- lib/PVLIB_Ruby/models/pv_module.rb
|
100
|
+
- lib/PVLIB_Ruby/plain_of_array_irradiance.rb
|
101
|
+
- lib/PVLIB_Ruby/pv_performance_characterization.rb
|
102
|
+
- lib/PVLIB_Ruby/pv_temperature.rb
|
103
|
+
- lib/PVLIB_Ruby/solar_ephemeris.rb
|
104
|
+
- lib/PVLIB_Ruby/version.rb
|
105
|
+
homepage: https://github.com/tadatoshi/PVLIB_Ruby
|
106
|
+
licenses:
|
107
|
+
- MIT
|
108
|
+
metadata:
|
109
|
+
allowed_push_host: https://rubygems.org
|
110
|
+
post_install_message:
|
111
|
+
rdoc_options: []
|
112
|
+
require_paths:
|
113
|
+
- lib
|
114
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '0'
|
124
|
+
requirements: []
|
125
|
+
rubyforge_project:
|
126
|
+
rubygems_version: 2.4.7
|
127
|
+
signing_key:
|
128
|
+
specification_version: 4
|
129
|
+
summary: PVLIB written in Ruby.
|
130
|
+
test_files: []
|