Skip to content

Commit

Permalink
Simulator with python (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
mj-xmr committed May 6, 2022
1 parent 123ce75 commit 49006b4
Show file tree
Hide file tree
Showing 8 changed files with 139 additions and 49 deletions.
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ requests==2.27.1
scipy==1.8.0
six==1.16.0
urllib3==1.26.9
python-dateutil==2.8.2
windows-curses==2.3.0 ; sys_platform == 'win32'
46 changes: 8 additions & 38 deletions src/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,17 @@

path_positions_txt = f"{PATH_POSITIONS_BASE}.txt"

def get_sun_positions():
def get_sun_positions(start_date, days_horizon=3, unpickle=True):
a = datetime.datetime.now()
start_date = datetime.datetime(2020, 1, 1)
path_positions_file_name = f"{PATH_POSITIONS_BASE}-{start_date.year}-{start_date.month}-{start_date.day}"
path_positions_file_name = f"{PATH_POSITIONS_BASE}-{start_date.year}-{start_date.month}-{start_date.day}-{days_horizon}"
path_positions = path_positions_file_name + ".dat"
if os.path.isfile(path_positions):
if unpickle and os.path.isfile(path_positions):
print("Reading from:", path_positions)
with open(path_positions, "rb") as handle:
pos = pickle.load(handle)
b = datetime.datetime.now()
else:
start = datetime.datetime(2020, 1, 1)
dti = pd.date_range(start, periods=30 * 8 * 24, freq="H")
else:
dti = pd.date_range(start_date, periods=days_horizon * 24, freq="H")

pos = pvlib.solarposition.get_solarposition(dti, sunrise_lib.LAT, sunrise_lib.LON)
b = datetime.datetime.now()
Expand Down Expand Up @@ -187,36 +185,6 @@ def print_relative(val, relative):
print("Overvolted = ", print_relative(self.num_overvolted, relative))
if self.num_overused > 0:
print("Overused = ", print_relative(self.num_overused, relative))

class BatterySimulatorCpp(BatterySimulator):
def iter_get_load(self, inp, out, hours=T_DELTA_HOURS):
discharge = hours * self.DISCHARGE_PER_HOUR
balance = inp - out - discharge
change = balance * MUL_POWER_2_CAPACITY
if change > MAX_USAGE:
#if out > MAX_USAGE: # A valid possibility
self.num_overused += 1
change = MAX_USAGE
#print(change)
self.load += change

if self.load > self.MAX_CAPACITY:
self.load = self.MAX_CAPACITY
self.num_overvolted += 1

if self.load < self.MIN_LOAD:
if self.initial_load:
self.num_undervolted_initial += 1
else:
self.num_undervolted += 1
if self.load < 0:
self.load = 0

if self.initial_load:
if self.load > self.MIN_LOAD:
self.initial_load = False

return self.get_load()

def get_usage_endor_example(available):
# TODO: use the available power wisely
Expand Down Expand Up @@ -330,7 +298,9 @@ def run_algo(elev, show_plots, algo):


def test(show_plots=False):
pos = get_sun_positions()
start_date = datetime.datetime(2020, 1, 1)
days_horizon = 30 * 9
pos = get_sun_positions(start_date, days_horizon)
#print(pos)

proc = proc_data(pos)
Expand Down
35 changes: 27 additions & 8 deletions src/opti-lib/src/OptiEnProfitSubject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include "GnuplotIOSWrap.h"

#include <Ios/Ofstream.hpp>
#include <Math/GeneralMath.hpp>
#include <Util/CoutBuf.hpp>
#include <Util/ToolsMixed.hpp>
Expand All @@ -29,8 +30,8 @@ OptiSubjectEnProfit::~OptiSubjectEnProfit()

struct Computer
{
double cores = 1;
double wattPerCore = 10;
double cores = 2;
double wattPerCore = 11.5;
double hashPerCore = 250;
double scalingFactor = 0.85;
double GetHashRate(double freqGhz) const
Expand Down Expand Up @@ -100,7 +101,7 @@ double OptiSubjectEnProfit::GetVerbose(const double * inp, int n, bool verbose)
CorPtr<ISimulatorTS> psim = TSUtil().GetSimPred(m_period, fun->GetOptiVec(), m_startEndFrame);
*/
//LOG << n << Nl;
Computer comp;
Computer comp; /// TODO: This has to become an array of computers, configurable by the User.
BatterySimulation battery;
double sum = 0;
double penalitySum = 0;
Expand Down Expand Up @@ -186,7 +187,7 @@ double OptiSubjectEnProfit::GetVerbose(const double * inp, int n, bool verbose)
LOGL << ": New goal = " << sumAdjusted << ", m_sumMax = " << m_sumMax << ", penality = " << penality << ", after " << 0 << " iterations\n";

BatterySimulation batteryCopy;
VecD hashes, loads, penalityUnder, input, prod, hashrateBonus;
VecD hashes, loads, penalityUnder, input, prod, hashrateBonus, usages;
for (int i = 0; i < n; ++i)
{
/// TODO: Remove duplication
Expand All @@ -200,24 +201,30 @@ double OptiSubjectEnProfit::GetVerbose(const double * inp, int n, bool verbose)

const double load = batteryCopy.iter_get_load(m_dataModel.GetPowerProduction(i), usage);

usages.Add(usage);
input.Add(val);
loads.Add(load);
prod.Add(m_dataModel.GetPowerProduction(i));
hashes.Add(sum);
hashrateBonus.Add(HashrateBonus(i % 24));
}
m_usages = usages;
m_input = input;
m_loads = loads;
m_prod = prod;
m_hashes = hashes;
m_hashrateBonus = hashrateBonus;
//ToolsMixed().SystemCallWarn("clear", __PRETTY_FUNCTION__);
GnuplotPlotTerminal1d(hashes, "hashes", 1, 0.5);
GnuplotPlotTerminal1d(loads, "battery", 1, 0.5);
OutputVar(hashes, "hashrates");
OutputVar(loads, "battery");
OutputVar(usages, "usage", false);

//GnuplotPlotTerminal1d(input, "input", 1, 0.5);
GnuplotPlotTerminal1d(prod, "prod", 1, 0.5);
GnuplotPlotTerminal1d(hashrateBonus, "hashrateBonus", 1, 0.5);



}
}
}
Expand All @@ -226,6 +233,17 @@ double OptiSubjectEnProfit::GetVerbose(const double * inp, int n, bool verbose)
//return -sum;
}

void OptiSubjectEnProfit::OutputVar(const EnjoLib::VecD & data, const EnjoLib::Str & descr, bool plot) const
{
if (plot)
{

GnuplotPlotTerminal1d(data, descr, 1, 0.5);
}
Ofstream fout("/tmp/soloptout-" + descr + ".txt");
fout << data.Print() << Nl;
}

/*
void OptiSubjectEnProfit::UpdateOutput()
{
Expand Down Expand Up @@ -259,13 +277,14 @@ EnjoLib::Array<EnjoLib::OptiMultiSubject::Bounds> OptiSubjectEnProfit::GetBounds

double OptiSubjectEnProfit::HashrateBonus(int hour) const
{
/// TODO: This is meant to be dynamically read from tsqsim
if (hour > 10 && hour < 16)
{
return 0.90;
return 0.97;
}
else if (hour > 18)
{
return 1.10;
return 1.03;
}
else
{
Expand Down
3 changes: 2 additions & 1 deletion src/opti-lib/src/OptiEnProfitSubject.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,10 @@ class OptiSubjectEnProfit : public EnjoLib::OptiMultiSubject // IOptiSubject

//STDFWD::vector<OptiVarF> GetOptiVarsResult() override { return m_optiFloatResult; }
void UpdateOutput();
EnjoLib::VecD m_hashes, m_loads, m_penalityUnder, m_input, m_prod, m_hashrateBonus;
EnjoLib::VecD m_hashes, m_loads, m_penalityUnder, m_input, m_prod, m_hashrateBonus, m_usages;

double HashrateBonus(int hour) const;
void OutputVar(const EnjoLib::VecD & data, const EnjoLib::Str & descr, bool plot = true) const;

protected:

Expand Down
5 changes: 3 additions & 2 deletions src/opti-lib/src/OptimizerEnProfit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ void OptimizerEnProfit::operator()()
int alreadyCombined = 0;
for (int i = 0; i < maxEl; ++i)
{
const int minHoursTogether = 2;
const int minHoursTogether = 3; /// TODO: This should be computer's parameter or user's tolerance
const int minHoursTogetherHalf = GMat().round(minHoursTogether/2.0);
bool cont = false;
const int index = GMat().round(rmath.Rand(0, horizonHours-0.999));
//binary[index] = binary[index] == 0 ? 1 : 0;
Expand All @@ -131,7 +132,7 @@ void OptimizerEnProfit::operator()()
hashStr[index] = bitC;
if (bit == 1)
{
for (int j = index - minHoursTogether; j <= index + minHoursTogether; ++j)
for (int j = index - minHoursTogetherHalf; j <= index + minHoursTogetherHalf; ++j)
{
if (j < 0 || j >= horizonHours)
{
Expand Down
88 changes: 88 additions & 0 deletions src/prod.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# https://stackoverflow.com/a/11236372
# apt install gfortran libffi-dev
# pip3 install pvlib ephem pytz beautifulsoup4 cairosvg wget requests Pillow


import os
import datetime
import pandas as pd
import time
import traceback
import argparse
import pickle
import numpy as np
import dateutil

from pytz import timezone
from matplotlib import pyplot as plt

import sunrise_lib
import generator
import kraken
from profitability import POW_Coin

from python_json_config import ConfigBuilder

DATE_NOW_STR = sunrise_lib.DATE_NOW.isoformat()
DEFAULT_HORIZON_DAYS = 3

def get_args():
parser = argparse.ArgumentParser()
parser.add_argument('-s', '--start-date', default=DATE_NOW_STR, type=str, help="Start date, ISO format (default: {})".format(DATE_NOW_STR))
parser.add_argument('-d', '--days-horizon', default=DEFAULT_HORIZON_DAYS, type=int, help="Horizon in days (default: {})".format(DEFAULT_HORIZON_DAYS))
#parser.add_argument('-v', '--verbose', default=TESTING, action='store_true', help="Test (default: OFF)")
return parser.parse_args()


class BatterySimulatorCpp(generator.BatterySimulator):
def __init(self):
pass

def run(self):
basePath = 'build/src/opti/opti' # TODO: Pass on days horizon
path = basePath
if not os.path.isfile(path):
path = '../' + path
result = sunrise_lib.run_cmd(path, True)
if result.returncode != 0:
raise RuntimeError("Failed to run opti")

basePathIn = "/tmp/soloptout-{}.txt"

self.hashrates = np.loadtxt(basePathIn.format('hashrates'))
self.loads = np.loadtxt(basePathIn.format('battery'))
self.usage = np.loadtxt(basePathIn.format('usage'))

def get_usage_prod(available):
bat_sim = BatterySimulatorCpp()
bat_sim.run()
hashrates = bat_sim.hashrates
loads = bat_sim.loads
usage = bat_sim.usage
incomes = [0]* len(available)
costs = [0]* len(available)
effs = [0]* len(available)

return "Production", hashrates, usage, loads, bat_sim, incomes, costs, effs

def run_main(elev, show_plots):
generator.run_algo(elev, show_plots, get_usage_prod)
generator.run_algo(elev, show_plots, generator.get_usage_simple)


def main(args):
start_date = dateutil.parser.parse(args.start_date)
pos = generator.get_sun_positions(start_date, args.days_horizon, unpickle=False)
#print(pos)
show_plots = True
proc = generator.proc_data(pos)
elev = generator.extr_data(proc)
print(elev)
run_main(elev, show_plots)

if __name__ == "__main__":
args = get_args()
main(args)
1 change: 1 addition & 0 deletions src/sunrise_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ def read_file(fname):
return fin.read()

def run_cmd(cmd, print_result=False):
print("Running command:\n" + cmd)
result = run(cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True)
if print_result:
print(result.returncode, result.stdout, result.stderr)
Expand Down
9 changes: 9 additions & 0 deletions util/ci-debian.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@ GEN="CodeBlocks - Unix Makefiles"

#cd externals/tsqsim/
#./util/prep-env.sh
#./util/prep-env.sh
#./util/deps-pull.sh
#./util/deps-build.sh
#./ci-default
#cd ../..

#./util/deps-build.sh
#./ci-default
#cd ../..

python3 src/tests.py
# Now test unpickling:
python3 src/tests.py
Expand All @@ -23,3 +28,7 @@ export R_HOME=/usr/lib/R && export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$R_HOME/lib
src/opti/opti --help
src/opti/opti --horizon-days 3 --start-day 30
src/opti/opti --help

echo "Testing the entire production chain:"
cd ..
python3 src/prod.py

0 comments on commit 49006b4

Please sign in to comment.