diff --git a/CHANGELOG.md b/CHANGELOG.md index 84f8eae170..9ae38fd2a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -108,6 +108,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix implausible test cases of HpModelSpec [#1042](https://github.com/ie3-institute/simona/issues/1042) - Refactoring to only use 'lastHpState' and 'relevantData' for 'ThermalGrid' calculations [#916](https://github.com/ie3-institute/simona/issues/916) - Refactor thermal calcRelevantData [#1051](https://github.com/ie3-institute/simona/issues/1051) +- Changed to the psdm `LoadProfileSource` [#951](https://github.com/ie3-institute/simona/issues/951) ### Fixed - Fix rendering of references in documentation [#505](https://github.com/ie3-institute/simona/issues/505) diff --git a/input/samples/vn_simona/vn_simona.conf b/input/samples/vn_simona/vn_simona.conf index b911acf2c1..b64cbeab1b 100644 --- a/input/samples/vn_simona/vn_simona.conf +++ b/input/samples/vn_simona/vn_simona.conf @@ -34,6 +34,10 @@ simona.input.grid.datasource.csvParams = { isHierarchic: false } +simona.input.loadprofile.datasource = { + loadBuildIns = true +} + simona.input.weather.datasource = { scheme = "icon" sampleParams.use = true diff --git a/src/main/resources/config/config-template.conf b/src/main/resources/config/config-template.conf index 6c9d886711..36c69dd100 100644 --- a/src/main/resources/config/config-template.conf +++ b/src/main/resources/config/config-template.conf @@ -241,10 +241,23 @@ simona.input.weather.datasource = { gridModel = "string" | "icon" #@optional csvParams = BaseCsvParams - #@optional - sampleParams = { - use: "boolean" | true # dummy parameter to allow sample data - } + #@optional + sampleParams = { + use: "boolean" | true # dummy parameter to allow sample data + } + #@optional + sqlParams = { + jdbcUrl: string + userName: string + password: string + tableName: string + schemaName: string | "public" + } + } +} +simona.input.loadprofile.datasource = { + #@optional + csvParams = BaseCsvParams #@optional sqlParams = { jdbcUrl: string @@ -253,7 +266,6 @@ simona.input.weather.datasource = { tableName: string schemaName: string | "public" } - } } ################################################################## # Output Parameters diff --git a/src/main/resources/load/random_load_parameters.csv b/src/main/resources/load/random_load_parameters.csv deleted file mode 100644 index 5a90edd42b..0000000000 --- a/src/main/resources/load/random_load_parameters.csv +++ /dev/null @@ -1,97 +0,0 @@ -kSa,kSu,kWd,mySa,mySu,myWd,sigmaSa,sigmaSu,sigmaWd,quarterHour -0.266806721687317,0.295997023582459,0.279087692499161,0.0610353946685791,0.0630703344941139,0.053140863776207,0.0357091873884201,0.0370676517486572,0.0293692331761122,0 -0.281179457902908,0.299608528614044,0.275292456150055,0.0560021996498108,0.058424074202776,0.0498424917459488,0.0319067053496838,0.0334825366735458,0.0265011098235846,1 -0.275563269853592,0.29670587182045,0.252942383289337,0.0528385005891323,0.0547995530068874,0.0472154095768929,0.0286294519901276,0.0310499873012304,0.0245211906731129,2 -0.268669873476028,0.278122246265411,0.222294941544533,0.0497889705002308,0.0522574931383133,0.0455464050173759,0.0267868731170893,0.0290201343595982,0.0230409931391478,3 -0.254242599010468,0.261944204568863,0.198370113968849,0.0480474047362804,0.0499211102724075,0.0444160588085651,0.025010583922267,0.0268981643021107,0.0216859001666307,4 -0.253653794527054,0.259219467639923,0.183828011155128,0.0465105324983597,0.0482752695679665,0.0433471724390984,0.0237453784793615,0.0254230201244354,0.0208296440541744,5 -0.232547923922539,0.22350800037384,0.151045009493828,0.0453270114958286,0.047629676759243,0.042874664068222,0.0228410512208939,0.0238813851028681,0.0201559588313103,6 -0.203599318861961,0.210258424282074,0.134497836232185,0.0445506721735001,0.0461143814027309,0.0425662696361542,0.0219020489603281,0.0230216048657894,0.0196861978620291,7 -0.161462053656578,0.199696600437164,0.119271591305733,0.0445012860000134,0.0455127842724323,0.0422079116106033,0.0207479763776064,0.0222345236688852,0.019109807908535,8 -0.132398754358292,0.181738555431366,0.0919454470276833,0.0439879409968853,0.0447122938930988,0.0418674796819687,0.0208074823021889,0.0220435112714767,0.0189618747681379,9 -0.141700059175491,0.172467365860939,0.089320495724678,0.0432550236582756,0.044278547167778,0.04146458953619,0.0204773582518101,0.0210072007030249,0.0188128501176834,10 -0.13490092754364,0.168581023812294,0.0863394215703011,0.0434850119054317,0.0437624379992485,0.0414730608463287,0.0204034727066755,0.0206510350108147,0.0187746584415436,11 -0.12891773879528,0.141391009092331,0.0815763771533966,0.0438057892024517,0.0438031405210495,0.0417981892824173,0.0202947575598955,0.020810017362237,0.0187485739588737,12 -0.0865642353892326,0.133543252944946,0.0992388054728508,0.0434134155511856,0.0436179116368294,0.0411902405321598,0.0195998474955559,0.0202769655734301,0.0186050590127707,13 -0.107326254248619,0.131506875157356,0.092495284974575,0.0422008074820042,0.0430818535387516,0.0411793626844883,0.0194479618221521,0.0201587229967117,0.0184435490518808,14 -0.12365210801363,0.138605788350105,0.0902709886431694,0.0423581749200821,0.0430288463830948,0.0415664203464985,0.0197580717504025,0.0195529088377953,0.018659807741642,15 -0.133885741233826,0.116305366158485,0.0866493508219719,0.0421508848667145,0.0430956780910492,0.0417333245277405,0.0191863905638456,0.0196987725794315,0.018691623583436,16 -0.114493139088154,0.109669730067253,0.11562417447567,0.0423808842897415,0.0430857576429844,0.0414095669984818,0.0193791724741459,0.0192934311926365,0.0187153313308954,17 -0.106474287807941,0.107617124915123,0.141828700900078,0.041958749294281,0.0426791086792946,0.0417547412216663,0.0191276986151934,0.0195350497961044,0.0195536445826292,18 -0.111373528838158,0.0949468687176705,0.143573239445686,0.0423438511788845,0.0426821634173393,0.0423176772892475,0.0193268302828074,0.0194729138165712,0.0196764413267374,19 -0.129695281386375,0.111747220158577,0.146539300680161,0.042476549744606,0.0431345589458942,0.0430354326963425,0.0200043357908726,0.0198324266821146,0.0201929099857807,20 -0.124765977263451,0.126777052879333,0.181237012147903,0.0424391217529774,0.0431524105370045,0.0431922376155853,0.0196738373488188,0.0196389146149158,0.0208003968000412,21 -0.166463151574135,0.128424167633057,0.222479611635208,0.0426704697310925,0.0434540957212448,0.0447872504591942,0.020222594961524,0.0205539185553789,0.0225220061838627,22 -0.15489549934864,0.121535487473011,0.279077500104904,0.0428037717938423,0.0438099093735218,0.0461413823068142,0.0201477259397507,0.0211283396929502,0.0241765789687634,23 -0.149270266294479,0.145163252949715,0.314658939838409,0.0454631112515926,0.0458601377904415,0.0499203614890575,0.0224028695374727,0.0220234617590904,0.0277903340756893,24 -0.190895333886147,0.153644308447838,0.411636203527451,0.0459029302000999,0.0457030460238457,0.0538441687822342,0.0229767691344023,0.0218722112476826,0.0328027196228504,25 -0.191453665494919,0.150224968791008,0.423471629619598,0.0472969971597195,0.047640148550272,0.0605787634849548,0.0232080388814211,0.0226516369730234,0.0382906869053841,26 -0.224633768200874,0.169333130121231,0.409934967756271,0.0484098829329014,0.0481277145445347,0.0650452002882957,0.0246889032423496,0.0234444178640842,0.0414291992783546,27 -0.23322768509388,0.195868730545044,0.387721389532089,0.0501904785633087,0.0495214238762856,0.0688034743070602,0.0260085947811604,0.0247648935765028,0.0444995686411858,28 -0.280531793832779,0.242264837026596,0.378016442060471,0.0515176840126514,0.0500866919755936,0.0698312446475029,0.0280240289866924,0.0263900514692068,0.044551245868206,29 -0.332535684108734,0.286791861057281,0.414923667907715,0.0538357272744179,0.0514319129288197,0.0683940351009369,0.0305218864232302,0.0274843797087669,0.0439568608999252,30 -0.356295973062515,0.335564076900482,0.428863227367401,0.055597260594368,0.0545664429664612,0.0673834607005119,0.0327046699821949,0.0305836349725723,0.0435638092458248,31 -0.381627827882767,0.356647431850433,0.41985896229744,0.0583727583289146,0.0575905330479145,0.066506527364254,0.0358871892094612,0.0332306325435638,0.042742770165205,32 -0.427426189184189,0.375110238790512,0.42010372877121,0.0632952600717545,0.0606768690049648,0.0665018707513809,0.0411307103931904,0.0364755131304264,0.0432239063084126,33 -0.418004125356674,0.411366045475006,0.413253307342529,0.0678070187568665,0.0630642250180244,0.0660132020711899,0.0440355911850929,0.0392848961055279,0.0416399873793125,34 -0.480116784572601,0.443256080150604,0.430845767259598,0.0709096193313599,0.0660979822278023,0.0664969906210899,0.0474613010883331,0.0422324053943157,0.0427436977624893,35 -0.492366999387741,0.433596074581146,0.438405454158783,0.0713961571455002,0.0694489479064941,0.0661367774009705,0.0493352487683296,0.0453926883637905,0.0428525730967522,36 -0.488669961690903,0.450059473514557,0.43893027305603,0.0731654316186905,0.0709325894713402,0.0653173625469208,0.050234030932188,0.0470801331102848,0.0422966778278351,37 -0.476427853107452,0.453100442886353,0.445331513881683,0.0735458433628082,0.0721306875348091,0.0635278075933456,0.0500484444200993,0.048948809504509,0.0411276556551456,38 -0.447788894176483,0.462226986885071,0.42346379160881,0.0715678483247757,0.0739922747015953,0.0632445514202118,0.0481005422770977,0.0500194020569324,0.0407665930688381,39 -0.448646247386932,0.481568813323975,0.437915831804276,0.0719510018825531,0.0743175819516182,0.0634156614542007,0.0479790642857552,0.0502395480871201,0.0411010235548019,40 -0.45268851518631,0.463462620973587,0.44245383143425,0.0715559348464012,0.0745281055569649,0.0629907771945,0.0486291088163853,0.0509366057813168,0.0407464392483234,41 -0.472129553556442,0.458590388298035,0.438809961080551,0.0714246481657028,0.0753235965967178,0.0624452345073223,0.0488574244081974,0.0513948574662209,0.0403173267841339,42 -0.465200632810593,0.482033789157867,0.437554955482483,0.07085170596838,0.0747058689594269,0.0618916675448418,0.0474738143384457,0.0496549904346466,0.0399090498685837,43 -0.463072091341019,0.485370546579361,0.430398672819138,0.070897750556469,0.0753868967294693,0.061828400939703,0.0477456152439117,0.0515857115387917,0.039817675948143,44 -0.504301607608795,0.495000720024109,0.421750038862228,0.071284644305706,0.0756229311227798,0.0619377642869949,0.0486843213438988,0.0521590225398541,0.0400484018027782,45 -0.494013756513596,0.502004504203796,0.431962072849274,0.0704603344202042,0.075153686106205,0.0622560419142246,0.04722835496068,0.0514012612402439,0.039880596101284,46 -0.480952113866806,0.475219011306763,0.427944034337997,0.0712133646011353,0.0757130458950996,0.0625443011522293,0.0476561672985554,0.0518420785665512,0.0402946658432484,47 -0.4861159324646,0.516651332378387,0.448401153087616,0.0710133090615273,0.0765742212533951,0.0628931447863579,0.0490181632339954,0.0527153052389622,0.0413180962204933,48 -0.505605041980743,0.523734390735626,0.470708280801773,0.0711066871881485,0.0766011476516724,0.0636289045214653,0.0492411740124226,0.0540735982358456,0.0423084460198879,49 -0.513229548931122,0.550731658935547,0.487264752388,0.0714162662625313,0.0776983126997948,0.0654886662960052,0.0496645383536816,0.0559630356729031,0.0443988926708698,50 -0.523128151893616,0.541519641876221,0.508733510971069,0.0719589814543724,0.0785757303237915,0.0667508617043495,0.0495718084275723,0.0566712841391563,0.0457895845174789,51 -0.505810618400574,0.511520206928253,0.512334108352661,0.0727487280964851,0.078390434384346,0.0674923211336136,0.0494792275130749,0.0563753582537174,0.0470927283167839,52 -0.523990094661713,0.521157741546631,0.502269566059113,0.0725650414824486,0.0767486467957497,0.0678139925003052,0.0510885566473007,0.0540808290243149,0.047186266630888,53 -0.505142688751221,0.510516762733459,0.473371058702469,0.0712223723530769,0.0750300288200378,0.067938931286335,0.0487538911402225,0.0511478036642075,0.0453384555876255,54 -0.49169534444809,0.497860074043274,0.440884381532669,0.0709625855088234,0.0743403211236,0.0676973983645439,0.0484340563416481,0.0499401353299618,0.0442236810922623,55 -0.449224293231964,0.49097341299057,0.43197637796402,0.0712493658065796,0.0736074447631836,0.0669783055782318,0.0481879562139511,0.0487863756716251,0.0432323180139065,56 -0.462083280086517,0.458611458539963,0.420163929462433,0.0710224434733391,0.0725374296307564,0.0672811642289162,0.0468299798667431,0.0484821572899818,0.0426194556057453,57 -0.466422975063324,0.431519895792007,0.411749631166458,0.0716194584965706,0.0718199908733368,0.0670692101120949,0.0491987094283104,0.0477711223065853,0.0421844013035297,58 -0.470720857381821,0.452195525169373,0.402273863554001,0.0716342553496361,0.0719970166683197,0.0668240934610367,0.0485116802155972,0.0467447564005852,0.0414865128695965,59 -0.464846402406693,0.438703387975693,0.404926747083664,0.0720167830586433,0.0727354884147644,0.0666147843003273,0.0477447547018528,0.0472656972706318,0.0414320714771748,60 -0.466590017080307,0.44343689084053,0.405802458524704,0.0720655098557472,0.072361558675766,0.0671483352780342,0.0480720065534115,0.0469786375761032,0.0417016632854939,61 -0.461378246545792,0.444682002067566,0.396851778030396,0.0718182176351547,0.0714400708675385,0.0669265314936638,0.047651831060648,0.0463756546378136,0.0415653325617313,62 -0.461525678634644,0.406780183315277,0.386990875005722,0.0716791599988937,0.0715874135494232,0.0669709593057632,0.0474572516977787,0.0466903038322926,0.0415439382195473,63 -0.447014361619949,0.414845287799835,0.384565651416779,0.0707539543509483,0.071328230202198,0.0670299381017685,0.0461493171751499,0.045212522149086,0.0411532782018185,64 -0.425164729356766,0.408224016427994,0.378880858421326,0.0712318941950798,0.0714204683899879,0.0676426440477371,0.0461742058396339,0.0446786060929298,0.0415577478706837,65 -0.406535506248474,0.39065757393837,0.390029609203339,0.0721957013010979,0.0714574679732323,0.0679289177060127,0.0468821786344051,0.0455186292529106,0.0415354371070862,66 -0.429334431886673,0.421460598707199,0.386443197727203,0.0720773190259933,0.0723162442445755,0.068557009100914,0.0471111163496971,0.0465410239994526,0.0424121767282486,67 -0.408385515213013,0.423402607440948,0.384852766990662,0.0731761381030083,0.0739946663379669,0.0693683549761772,0.0479194596409798,0.0479996241629124,0.043307401239872,68 -0.450928092002869,0.433510303497314,0.387267470359802,0.0732240453362465,0.0757285431027412,0.070844329893589,0.0488691031932831,0.0505488254129887,0.0446740724146366,69 -0.42838642001152,0.458660960197449,0.397508889436722,0.0737349465489388,0.076910100877285,0.0725433081388473,0.0485723651945591,0.0513449050486088,0.0462205372750759,70 -0.457528233528137,0.455272883176804,0.401177316904068,0.0746021792292595,0.0782865285873413,0.0737910494208336,0.0504703745245934,0.0534177497029305,0.0476498752832413,71 -0.477989226579666,0.449814409017563,0.415226876735687,0.0757969841361046,0.0819070339202881,0.0762551799416542,0.0521196164190769,0.0565894842147827,0.0500268675386906,72 -0.471660286188126,0.483049720525742,0.41699144244194,0.0772641450166702,0.0833683088421822,0.0779938697814941,0.0538835860788822,0.0582899935543537,0.0516200289130211,73 -0.462039291858673,0.484837472438812,0.417837411165237,0.0793179050087929,0.0855541229248047,0.0807979106903076,0.0558264814317226,0.0610172972083092,0.0537474788725376,74 -0.494239538908005,0.474657744169235,0.410433322191238,0.080167755484581,0.0883776918053627,0.0834952890872955,0.0563570857048035,0.0627634450793266,0.0555526539683342,75 -0.481115281581879,0.4631627202034,0.405094832181931,0.0810613483190537,0.0898661091923714,0.085809163749218,0.0574533082544804,0.063525564968586,0.0566239841282368,76 -0.481957763433456,0.444837510585785,0.392141699790955,0.0815427601337433,0.0908975899219513,0.0876085460186005,0.0575459823012352,0.0625557452440262,0.0570836253464222,77 -0.46474814414978,0.416785925626755,0.384859055280685,0.0834897756576538,0.0924627110362053,0.0896750018000603,0.0571116954088211,0.0623503513634205,0.0581204779446125,78 -0.446739017963409,0.395158112049103,0.372885912656784,0.0857851952314377,0.0973858460783958,0.0923485606908798,0.0588888488709927,0.0653925687074661,0.0595557168126106,79 -0.425965458154678,0.364043891429901,0.341714292764664,0.0874398797750473,0.102611370384693,0.0957552641630173,0.0598357506096363,0.0677491873502731,0.0603233315050602,80 -0.401139229536056,0.364588558673859,0.329459756612778,0.0885151326656342,0.102042146027088,0.097852848470211,0.0583052486181259,0.0661919564008713,0.0615657567977905,81 -0.386102288961411,0.331744521856308,0.310481607913971,0.088083989918232,0.102688789367676,0.0995604023337364,0.056625984609127,0.064409427344799,0.0617730244994164,82 -0.352426558732986,0.287841856479645,0.287958323955536,0.0899283438920975,0.103480368852615,0.100912734866142,0.0579833500087261,0.0627572685480118,0.0609871000051498,83 -0.356676459312439,0.279404014348984,0.266594916582108,0.0904190465807915,0.104584082961082,0.10176607966423,0.0583710223436356,0.0623739063739777,0.0608399920165539,84 -0.353478521108627,0.253417283296585,0.25350296497345,0.0909158438444138,0.105099938809872,0.103342562913895,0.0577074661850929,0.0616596378386021,0.0614556968212128,85 -0.299452573060989,0.232542738318443,0.223194167017937,0.0930669605731964,0.105844244360924,0.104641474783421,0.0584431923925877,0.0609173066914082,0.0607141107320786,86 -0.257176458835602,0.220698863267899,0.218866571784019,0.0945220738649368,0.107813261449337,0.104784607887268,0.0576106905937195,0.0613766200840473,0.0604684762656689,87 -0.257824391126633,0.227517262101173,0.207409575581551,0.092997707426548,0.104204557836056,0.102655082941055,0.0560135245323181,0.0604200474917889,0.058957364410162,88 -0.238055855035782,0.211737111210823,0.201141089200974,0.0910771638154984,0.0975786820054054,0.0979374051094055,0.0540145039558411,0.0561005882918835,0.0565716363489628,89 -0.238567247986794,0.227371960878372,0.204761475324631,0.0879477933049202,0.0902053341269493,0.0918598845601082,0.0529557727277279,0.0529294200241566,0.0533599480986595,90 -0.216722473502159,0.234640255570412,0.217169404029846,0.0868481546640396,0.0833379998803139,0.0849183723330498,0.0509745627641678,0.0489254035055637,0.0499787591397762,91 -0.231909289956093,0.24814073741436,0.230379953980446,0.0836911797523499,0.0766035988926888,0.0774712786078453,0.0496042557060719,0.0455812141299248,0.0460172258317471,92 -0.223331198096275,0.256241410970688,0.256796300411224,0.0792895033955574,0.070108599960804,0.0709625855088234,0.0472096875309944,0.0411984920501709,0.0422120206058025,93 -0.246469661593437,0.287815362215042,0.283796429634094,0.0738609507679939,0.0632559433579445,0.0643703117966652,0.043757751584053,0.0369679853320122,0.0378340892493725,94 -0.270621925592422,0.283220708370209,0.280689835548401,0.068959467113018,0.0578584857285023,0.0592832416296005,0.0411073453724384,0.032867755740881,0.0339051820337772,95 \ No newline at end of file diff --git a/src/main/resources/load/standard_load_profiles.csv b/src/main/resources/load/standard_load_profiles.csv deleted file mode 100644 index 64780bf40f..0000000000 --- a/src/main/resources/load/standard_load_profiles.csv +++ /dev/null @@ -1,97 +0,0 @@ -g0SuSa,g0SuSu,g0SuWd,g0TrSa,g0TrSu,g0TrWd,g0WiSa,g0WiSu,g0WiWd,h0SuSa,h0SuSu,h0SuWd,h0TrSa,h0TrSu,h0TrWd,h0WiSa,h0WiSu,h0WiWd,l0SuSa,l0SuSu,l0SuWd,l0TrSa,l0TrSu,l0TrWd,l0WiSa,l0WiSu,l0WiWd,quarterHour -74.6,68.8,71.5,75.8,68.3,73.0,70.0,63.2,65.5,89.8,100.1,86.3,80.2,93.4,77.8,70.8,87.5,67.6,67.2,62.6,66.1,71.4,64.8,70.3,73.9,68.3,72.4,0 -76.2,67.4,69.0,76.7,66.5,70.1,73.0,61.0,62.6,84.9,92.5,76.9,75.1,86.8,69.6,68.2,81.1,60.8,65.6,60.6,63.1,69.4,62.7,67.1,73.0,66.0,69.4,1 -77.7,65.7,66.3,77.7,64.6,67.1,75.9,58.9,59.6,80.7,85.9,68.8,70.7,81.2,62.4,65.9,75.0,54.9,64.3,58.9,60.5,68.0,61.3,64.3,72.6,64.3,66.9,2 -78.5,63.5,63.5,78.5,62.6,64.5,77.6,57.0,57.0,76.6,79.9,62.4,66.6,75.7,56.6,63.3,69.1,49.9,63.0,57.4,58.4,66.7,60.2,62.1,72.3,63.0,64.8,3 -77.9,60.9,60.9,78.5,60.3,62.3,77.1,55.3,54.8,71.7,74.1,58.0,62.3,70.1,52.5,59.5,63.4,46.2,61.4,56.2,56.7,65.3,59.2,60.3,71.5,62.1,63.2,4 -76.3,58.0,58.6,77.8,57.9,60.6,75.0,53.7,53.1,66.6,68.7,55.3,58.0,64.5,49.7,55.0,58.2,43.6,59.7,55.2,55.4,63.9,58.2,59.1,70.4,61.4,61.9,5 -74.1,55.4,56.6,76.6,55.5,59.2,72.1,52.1,51.7,61.6,63.9,53.6,54.1,59.3,47.9,50.5,53.6,41.9,58.0,54.4,54.4,62.4,57.3,58.1,69.1,60.8,61.0,6 -71.9,53.3,55.1,74.7,53.3,57.9,69.1,50.5,50.5,57.4,59.9,52.4,50.8,54.9,46.6,46.6,49.9,40.8,56.5,53.7,53.7,61.1,56.5,57.4,67.6,60.2,60.2,7 -70.2,52.0,54.2,72.6,51.2,56.7,66.8,48.7,49.4,54.5,57.0,51.3,48.4,51.7,45.5,43.9,47.3,40.1,55.2,53.2,53.2,60.0,55.8,56.9,66.2,59.5,59.6,8 -68.9,51.4,53.7,70.4,49.5,55.6,65.1,46.9,48.5,52.6,55.0,50.3,46.8,49.4,44.5,42.3,45.5,39.6,54.2,52.7,52.7,59.1,55.3,56.4,64.8,58.8,59.2,9 -67.9,51.0,53.4,68.5,48.0,54.7,64.1,45.2,47.9,51.4,53.5,49.2,45.7,47.8,43.8,41.4,44.2,39.4,53.4,52.3,52.3,58.3,54.9,56.0,63.4,58.0,58.8,10 -67.3,50.5,53.3,67.3,46.7,54.2,63.5,43.9,47.7,50.8,52.4,48.3,44.9,46.6,43.3,40.8,43.3,39.1,52.8,51.9,51.9,57.4,54.7,55.6,62.1,57.4,58.4,11 -66.9,49.6,53.1,67.0,45.7,54.1,63.4,43.0,47.9,50.3,51.5,47.5,44.4,45.5,43.0,40.3,42.4,38.8,52.3,51.4,51.4,56.6,54.6,55.1,60.7,57.0,57.9,12 -66.7,48.5,53.2,67.5,44.9,54.6,63.6,42.5,48.7,50.0,50.8,46.9,43.9,44.5,43.0,39.9,41.5,38.6,51.8,50.9,51.0,55.7,54.6,54.5,59.4,56.7,57.4,13 -66.8,47.5,54.1,68.3,44.3,55.8,64.0,42.2,50.2,49.9,50.2,46.5,43.5,43.8,43.1,39.5,40.7,38.3,51.4,50.5,50.5,55.1,54.7,54.1,58.3,56.6,56.9,14 -67.3,46.7,56.1,69.1,43.9,57.9,64.5,42.0,52.3,49.9,49.9,46.6,43.3,43.3,43.3,39.1,40.0,38.3,50.9,50.0,50.0,54.7,54.7,53.7,57.4,56.5,56.5,15 -68.0,46.4,59.4,69.7,43.6,60.9,65.0,41.9,55.1,50.1,49.9,47.1,43.1,43.1,43.4,38.8,39.3,38.4,50.5,49.6,49.6,54.6,54.5,53.6,56.9,56.4,56.3,16 -69.0,46.5,63.3,70.1,43.5,64.4,65.6,41.8,58.2,50.4,50.0,48.0,43.1,43.1,43.7,38.5,38.8,38.8,50.2,49.4,49.4,54.7,54.3,53.7,56.8,56.5,56.3,17 -70.0,46.6,67.2,70.5,43.6,68.0,66.3,41.8,61.2,50.7,50.1,49.3,43.1,43.2,44.2,38.3,38.5,39.3,50.0,49.5,49.5,55.1,54.3,54.0,57.0,56.8,56.6,18 -71.0,46.7,70.1,71.0,43.9,71.0,67.3,42.0,63.5,50.8,49.9,50.8,43.3,43.3,44.9,38.3,38.3,40.0,50.0,50.0,50.0,55.6,54.7,54.7,57.4,57.4,57.4,19 -71.8,46.6,71.4,71.8,44.5,73.2,68.5,42.6,65.0,50.8,49.5,52.7,43.6,43.3,46.3,38.5,38.3,40.9,50.3,51.2,51.2,56.1,55.5,55.6,58.2,58.5,58.8,20 -72.4,46.3,71.6,72.5,45.1,74.7,69.9,43.4,66.0,50.9,48.9,55.6,44.2,43.3,48.9,39.1,38.4,43.1,50.9,53.1,52.9,56.6,57.3,57.2,59.2,60.5,60.9,21 -72.8,46.0,71.2,73.0,45.6,75.7,71.4,44.4,67.1,51.6,48.4,60.5,45.4,43.2,53.7,40.3,38.7,47.7,51.7,56.0,55.3,57.4,60.3,59.8,60.5,63.7,63.8,22 -72.9,45.8,71.0,72.9,45.8,76.6,72.9,45.8,69.1,53.3,48.3,68.2,47.4,43.3,61.6,42.4,39.1,55.8,52.8,60.2,58.4,58.4,64.8,63.9,62.1,68.5,67.6,23 -73.0,45.9,71.5,72.2,45.6,77.7,74.4,47.4,72.5,56.2,48.7,79.2,50.5,43.5,72.9,45.6,39.7,68.0,54.4,65.8,62.2,59.9,71.3,69.8,64.1,75.3,72.5,24 -73.9,46.3,73.1,72.2,45.3,79.3,76.8,49.0,77.1,60.4,49.8,92.0,54.9,44.3,86.3,49.9,40.4,82.8,57.2,73.2,67.4,62.9,79.8,77.6,67.4,84.1,79.0,25 -77.0,47.2,75.9,74.4,45.2,81.9,81.0,50.4,82.9,65.8,51.9,104.7,60.7,46.0,100.1,55.3,41.3,98.0,62.2,82.6,74.8,68.8,90.6,87.6,72.9,94.8,87.7,26 -83.2,48.6,80.4,80.4,45.8,86.0,87.8,51.4,89.7,72.4,54.9,115.7,68.2,49.1,112.4,61.6,42.4,111.5,70.4,94.5,85.2,78.7,103.7,100.0,81.5,107.4,99.1,27 -93.3,50.5,86.6,91.0,47.2,91.9,98.0,51.8,97.6,80.0,59.2,123.5,77.5,53.9,121.8,68.9,44.0,121.6,82.4,108.8,98.9,93.4,119.3,114.8,93.9,121.9,113.6,28 -106.2,52.6,95.1,105.2,49.1,100.4,110.7,51.7,107.3,88.5,64.9,128.6,87.9,60.4,128.5,77.1,46.6,128.5,97.4,124.7,114.9,111.6,136.3,131.3,109.4,137.8,130.5,29 -120.5,54.3,106.3,121.2,51.0,111.9,124.8,51.2,119.9,97.4,72.3,132.0,98.6,68.8,132.9,86.1,51.1,132.7,114.2,141.0,131.8,131.4,153.8,148.6,126.9,155.0,148.9,30 -134.5,55.1,120.5,137.4,52.3,127.1,139.2,50.5,136.4,106.5,81.6,134.8,109.0,79.1,135.7,95.7,58.3,134.8,131.5,156.5,148.2,151.0,170.4,165.8,145.4,173.2,167.7,31 -147.2,54.8,137.8,152.1,52.8,145.9,153.0,49.6,157.1,115.6,92.9,137.8,118.4,91.1,137.2,105.8,68.6,135.4,148.2,170.1,162.6,168.6,185.3,181.9,163.8,191.7,185.8,32 -157.8,53.9,156.3,164.9,52.9,166.2,165.4,49.0,179.5,124.4,105.6,140.7,126.7,104.3,137.7,115.8,81.3,134.8,162.8,180.5,173.9,183.2,197.0,195.1,181.0,208.6,201.7,33 -166.2,53.2,173.4,175.1,53.1,185.2,175.9,48.8,200.5,132.8,119.0,143.2,133.8,118.0,137.7,124.9,95.2,133.1,173.8,186.6,180.6,193.7,204.6,203.4,195.6,221.4,213.6,34 -171.9,53.3,186.9,182.2,54.2,200.0,184.1,49.5,216.8,140.7,132.3,144.8,139.8,131.5,137.3,132.3,109.0,130.7,179.7,187.1,181.6,199.2,206.6,204.7,206.6,227.9,219.5,35 -175.0,54.7,195.0,186.1,56.4,208.5,189.5,51.3,226.2,147.8,144.8,145.3,144.7,144.2,136.9,137.6,121.9,127.7,179.6,181.6,176.1,198.9,202.4,198.0,212.9,226.4,218.5,36 -176.1,57.2,198.7,187.5,59.5,212.1,192.8,53.8,230.0,154.0,156.2,144.9,148.8,155.5,136.4,141.1,133.7,124.6,174.9,171.8,166.1,194.1,193.4,185.5,214.6,218.6,211.6,37 -176.3,60.0,199.8,187.7,62.7,212.6,195.1,56.8,230.4,158.9,166.0,143.8,152.4,165.3,135.7,143.3,144.4,121.5,167.2,160.1,154.1,186.2,181.5,170.6,212.1,206.6,200.8,38 -176.6,62.6,200.0,187.8,65.4,212.1,197.1,59.8,229.9,162.3,174.0,142.3,155.6,173.1,134.8,144.8,154.0,119.0,158.4,149.1,142.6,176.9,168.6,156.5,205.6,192.7,188.0,39 -177.7,64.5,200.6,188.7,67.2,212.2,199.8,62.6,230.0,164.1,180.0,140.8,158.9,178.8,133.7,146.0,162.6,117.3,150.1,140.8,133.8,167.6,156.5,145.9,195.9,178.9,175.0,40 -179.4,65.9,201.9,190.1,68.5,213.1,202.7,65.1,231.2,164.7,184.6,139.5,162.0,183.1,132.4,147.2,170.5,116.2,143.4,135.6,128.2,159.6,146.4,139.0,185.0,167.0,163.4,41 -181.3,67.0,203.6,191.9,69.6,214.6,205.4,67.2,233.0,165.0,188.7,138.5,164.9,187.0,131.4,148.4,178.0,115.7,139.1,133.7,125.8,154.0,139.6,135.5,175.4,158.1,154.5,42 -183.1,68.2,205.6,193.4,71.0,216.8,207.4,69.1,235.5,165.6,193.1,138.2,167.3,191.4,130.7,149.8,185.6,115.7,138.0,135.2,126.9,151.9,137.1,135.2,169.5,153.8,150.1,43 -184.4,69.7,207.6,194.5,73.1,219.2,208.4,70.8,238.1,167.1,198.3,138.6,169.2,197.0,130.6,151.5,193.3,116.1,140.4,139.9,131.2,153.9,139.4,137.5,168.9,154.6,150.7,44 -185.2,71.4,209.1,195.1,75.6,221.2,208.3,72.2,240.0,169.4,203.7,140.1,170.8,203.0,131.5,153.5,200.6,117.0,144.4,145.6,136.6,157.9,144.3,140.7,171.3,158.2,154.1,45 -185.3,73.2,209.6,195.3,78.2,222.0,207.4,73.5,240.4,172.4,208.7,142.6,172.5,208.5,133.6,156.0,206.6,118.7,147.5,149.8,140.9,161.3,148.8,143.1,173.5,161.5,157.4,46 -185.0,74.7,208.4,195.3,80.4,220.5,205.6,74.7,238.3,175.6,212.2,146.5,174.8,212.2,137.3,159.0,210.6,121.5,147.3,150.1,141.7,161.2,150.1,142.6,172.3,161.2,157.5,47 -184.1,76.0,205.1,194.9,81.9,216.3,203.0,76.0,233.0,179.0,213.7,151.5,177.7,213.5,142.6,162.4,211.8,125.4,142.0,144.6,137.4,155.8,146.0,138.2,165.4,155.2,152.2,48 -182.4,77.0,200.0,193.9,82.8,209.8,199.7,77.3,225.1,181.9,212.8,156.7,180.8,211.8,148.2,165.8,210.2,129.6,133.1,135.3,129.4,146.7,138.1,130.9,154.5,145.4,143.2,49 -179.7,77.8,193.8,191.7,83.2,201.9,195.6,78.4,215.7,183.7,209.0,160.7,183.1,207.0,152.8,168.4,205.9,133.0,122.7,124.5,119.9,135.9,128.5,122.5,142.0,134.4,132.7,50 -175.7,78.5,186.9,187.8,83.2,193.4,190.6,79.4,205.6,183.9,202.3,162.3,183.9,198.9,154.8,169.8,198.9,134.8,113.0,114.9,111.2,126.0,119.5,114.9,130.6,125.0,123.2,51 -170.1,79.1,179.9,181.9,82.8,185.0,184.6,80.1,195.7,182.1,192.4,160.5,182.7,187.6,153.2,169.4,189.6,134.2,105.7,108.4,104.8,118.5,112.9,109.4,122.1,119.4,116.5,52 -163.1,79.5,173.4,174.1,82.1,177.4,177.5,80.5,186.7,178.7,180.9,156.1,179.8,174.6,148.9,167.6,178.7,131.7,100.6,104.7,100.8,113.1,108.6,105.9,116.1,116.9,112.5,53 -154.6,79.3,168.1,164.8,80.9,170.9,168.9,80.3,179.2,174.1,169.2,150.2,175.8,161.7,143.2,164.8,167.3,128.0,96.8,102.8,98.5,108.8,105.7,103.9,111.6,116.1,110.3,54 -144.8,78.5,164.4,154.2,79.4,166.3,158.8,79.4,173.8,169.0,159.0,144.0,171.5,150.6,137.3,161.5,156.5,124.0,93.6,101.9,97.3,104.7,103.7,102.8,107.4,115.8,109.3,55 -133.9,76.8,163.0,142.6,77.5,163.9,147.2,77.8,171.0,163.7,151.4,138.4,167.2,142.8,132.4,158.1,147.0,120.2,90.3,101.1,96.5,100.0,102.0,102.1,102.9,114.8,108.8,56 -122.7,74.4,163.5,130.9,75.3,163.6,134.8,75.7,170.7,158.9,146.0,133.6,163.1,137.6,128.4,154.9,138.9,116.8,87.1,100.4,96.2,95.3,100.5,101.7,98.2,113.2,108.5,57 -112.0,71.8,165.4,119.8,73.1,165.1,122.7,73.4,172.7,154.6,141.6,129.4,159.2,133.9,124.8,151.8,132.1,113.7,84.4,99.7,96.2,91.1,99.2,101.4,94.0,111.3,108.4,58 -102.8,69.1,168.2,110.3,71.0,168.2,112.1,71.0,176.6,151.5,137.3,125.7,155.6,130.7,121.5,149.0,126.5,110.7,82.4,99.1,96.3,88.0,98.2,101.0,90.8,109.3,108.4,59 -95.7,66.8,171.5,102.7,69.2,172.5,103.8,68.9,182.1,149.6,132.4,122.4,152.3,127.1,118.1,146.5,122.0,107.9,81.5,98.6,96.7,86.6,97.4,100.4,88.9,107.5,108.3,60 -90.4,64.8,174.6,96.9,67.6,177.3,97.4,67.0,188.2,148.6,127.0,119.6,149.5,123.1,114.8,144.4,118.1,105.5,81.3,98.1,97.0,86.3,96.7,99.9,88.2,106.1,108.3,61 -86.4,63.1,177.2,92.1,66.1,181.4,92.6,65.2,193.9,148.0,121.6,117.4,147.2,119.0,111.7,142.7,114.8,103.5,81.4,97.7,97.2,86.7,96.1,99.4,88.0,105.4,108.6,62 -83.2,61.7,178.5,87.8,64.5,184.1,88.8,63.5,198.1,147.3,116.5,115.7,145.7,114.9,109.0,141.5,111.5,102.4,81.5,97.3,97.3,87.1,95.4,99.1,88.0,105.6,109.3,63 -80.3,60.4,178.2,83.6,62.8,184.5,85.6,62.0,200.1,146.2,112.2,114.6,145.0,111.0,106.9,140.9,108.3,102.2,81.3,96.9,97.0,87.1,94.7,99.2,87.9,106.9,110.6,64 -77.8,59.7,176.8,79.7,61.5,183.6,83.3,61.2,200.7,145.0,108.9,114.2,145.4,107.7,105.7,141.7,105.9,103.2,81.3,96.9,96.8,87.3,94.5,99.9,88.5,109.8,113.1,65 -75.9,59.7,175.1,76.9,61.2,182.1,82.4,61.8,200.9,144.1,106.7,114.6,146.7,105.6,105.5,144.9,105.2,105.6,81.8,97.5,97.1,88.5,95.8,101.6,90.9,114.4,117.4,66 -74.7,60.7,173.8,75.7,62.6,181.3,83.2,64.5,201.8,144.0,105.7,115.7,149.0,104.9,106.5,151.5,107.4,109.9,83.4,99.1,98.2,91.7,99.1,104.7,96.3,121.3,124.1,67 -74.4,63.0,173.2,76.5,65.9,181.5,85.8,69.5,204.0,144.9,106.1,117.6,152.3,106.0,109.1,161.9,112.9,116.0,86.6,102.1,100.6,97.4,105.2,109.4,105.5,130.7,133.7,68 -74.8,66.0,172.4,78.6,70.4,181.7,89.5,76.0,205.8,146.9,107.7,120.3,156.4,108.8,113.1,174.6,120.9,123.7,91.7,106.8,104.8,105.7,114.0,116.2,117.8,142.4,146.0,69 -75.6,69.2,170.2,81.1,75.0,180.0,93.4,82.6,205.5,150.0,110.4,123.9,161.5,113.2,118.3,187.4,130.0,132.6,99.2,113.7,111.5,116.2,125.3,125.6,132.7,156.1,160.5,70 -76.6,71.9,165.4,83.2,78.5,174.7,96.2,87.8,200.9,154.0,114.0,128.2,167.3,119.0,124.8,198.1,139.0,142.3,109.3,123.2,121.3,128.8,138.9,138.0,149.1,171.4,176.9,71 -77.6,73.9,157.1,84.1,80.1,164.8,97.4,90.8,190.7,158.8,118.3,133.2,173.7,126.0,132.4,205.2,146.8,152.4,122.1,135.4,134.4,142.9,154.5,153.5,166.4,187.8,194.5,72 -78.5,75.1,146.1,84.3,80.4,151.5,97.4,92.1,176.6,164.0,123.4,138.9,180.4,133.7,140.6,209.1,153.5,162.2,135.8,148.9,148.9,157.3,170.3,170.2,183.4,204.1,211.5,73 -79.1,76.0,133.8,84.1,80.2,137.0,96.8,92.3,160.5,169.2,129.2,145.1,186.7,141.5,149.1,211.1,159.7,171.2,148.6,161.8,162.6,170.7,184.6,185.8,199.0,218.5,226.1,74 -79.4,76.6,121.5,84.1,80.4,123.3,96.2,92.5,144.8,174.0,135.7,151.5,192.3,149.0,157.3,212.2,165.6,178.9,158.4,172.3,173.2,181.6,195.4,198.2,212.1,229.7,236.2,75 -79.4,77.3,110.1,84.7,81.4,112.1,96.3,93.1,131.3,177.8,142.8,157.9,196.5,155.5,164.9,213.2,171.5,184.7,163.6,178.8,178.9,188.7,201.4,205.5,221.5,236.2,240.4,76 -79.4,78.2,100.5,85.8,83.1,103.4,96.8,94.0,120.1,180.5,149.8,163.8,199.0,160.6,171.1,213.0,176.4,188.2,164.4,181.1,179.8,191.7,202.7,207.5,226.2,237.4,238.7,77 -79.6,79.2,93.2,87.3,85.1,97.3,97.2,94.6,111.3,181.8,155.7,168.3,199.4,163.6,175.2,210.4,179.1,188.9,161.5,179.2,176.5,190.6,199.9,204.7,224.9,232.9,231.5,78 -80.4,80.4,88.8,88.8,86.9,93.4,97.2,94.4,104.6,181.4,159.8,170.6,197.3,164.0,176.5,203.9,178.1,186.4,155.6,173.2,169.5,185.3,193.6,197.3,216.8,222.3,219.5,79 -81.9,81.6,87.4,90.1,88.1,91.6,96.3,93.0,100.0,179.3,161.4,170.4,192.4,161.5,174.5,192.9,172.9,180.7,147.4,163.4,159.6,175.8,184.4,185.9,201.3,205.7,203.4,80 -83.9,82.9,88.1,91.2,88.7,91.1,94.8,90.8,96.7,175.7,160.8,168.3,185.2,157.1,170.5,179.0,164.7,172.7,137.8,151.4,148.1,163.7,173.4,172.1,181.3,185.5,184.9,81 -85.7,83.8,89.6,91.7,88.6,91.1,92.8,88.0,94.1,171.0,159.0,165.3,176.6,152.2,165.7,164.4,155.6,163.9,127.9,138.9,136.4,150.5,161.6,157.7,160.3,164.7,166.1,82 -86.9,84.1,90.6,91.6,87.8,90.6,90.6,85.0,91.6,165.6,156.5,162.3,167.3,148.2,161.5,151.5,147.3,155.6,118.6,127.8,126.0,138.0,150.1,144.5,141.7,146.4,149.1,83 -87.0,83.7,90.2,90.6,86.4,89.2,88.5,82.1,88.5,160.1,153.9,160.1,157.9,145.9,158.9,141.9,141.4,148.9,110.7,119.5,117.9,127.4,139.6,133.8,128.3,132.6,135.6,84 -86.3,82.7,88.5,89.0,84.4,87.2,86.4,79.4,85.2,155.1,151.5,158.4,149.5,144.7,157.2,135.3,137.2,143.4,104.1,113.5,111.9,118.8,130.3,125.5,119.3,123.0,125.2,85 -84.9,81.2,86.3,87.1,82.0,85.0,84.3,76.9,82.1,151.1,149.3,156.8,142.9,143.6,155.4,131.0,133.7,138.4,98.5,108.8,107.1,111.6,122.1,118.8,113.2,116.3,117.3,86 -83.2,79.4,84.1,85.0,79.4,83.2,82.2,74.7,79.4,149.0,147.3,154.8,139.0,141.5,152.3,128.2,129.8,133.2,93.6,104.7,102.8,105.6,114.9,113.0,108.4,111.2,111.2,87 -81.3,77.5,82.4,83.1,76.7,82.1,80.1,72.8,77.6,148.9,145.4,151.9,138.3,137.5,147.2,126.1,124.8,127.2,88.9,100.2,98.5,100.3,108.4,107.7,103.7,106.6,106.1,88 -79.3,75.5,81.2,81.2,74.1,81.7,78.0,71.1,76.4,149.6,143.0,147.9,139.2,131.8,140.3,124.1,118.6,120.5,84.6,95.4,94.1,95.5,102.5,102.5,98.8,102.1,101.7,89 -77.4,73.3,80.3,79.4,71.5,81.6,75.9,69.3,75.6,149.4,139.2,142.5,139.5,124.7,132.1,121.6,111.6,113.3,80.6,90.4,89.6,90.9,97.0,97.6,93.9,97.6,97.7,90 -75.7,71.0,79.4,77.6,69.1,81.3,73.8,67.3,74.7,146.5,133.2,135.7,137.3,116.5,123.2,118.2,104.0,105.7,76.9,85.2,85.2,86.1,91.7,92.6,88.9,92.6,93.6,91 -74.1,68.6,78.4,75.7,67.0,80.6,71.7,65.0,73.7,139.8,124.4,127.2,131.1,107.6,114.0,113.4,96.2,98.0,73.4,80.0,81.0,81.1,86.4,87.7,84.0,87.2,89.1,92 -72.7,66.4,77.2,73.8,65.3,79.5,69.7,62.6,72.3,130.3,113.8,117.5,121.9,98.4,104.8,107.4,88.4,90.2,70.3,75.0,76.9,76.1,81.2,82.9,79.3,81.6,84.6,93 -71.3,64.6,75.7,71.9,64.1,77.9,67.6,60.4,70.5,119.5,102.5,107.1,111.5,89.2,95.6,100.8,80.7,82.5,67.4,70.5,73.1,71.5,76.4,78.3,75.0,76.5,80.1,94 -70.1,63.5,73.8,70.1,63.5,75.7,65.4,58.9,68.2,109.0,91.6,96.5,101.5,80.7,86.6,94.1,73.2,74.9,64.8,66.7,69.5,67.6,72.3,74.1,71.3,72.3,76.0,95 diff --git a/src/main/scala/edu/ie3/simona/agent/EnvironmentRefs.scala b/src/main/scala/edu/ie3/simona/agent/EnvironmentRefs.scala index 4b5c6c920d..1e1960f2fa 100644 --- a/src/main/scala/edu/ie3/simona/agent/EnvironmentRefs.scala +++ b/src/main/scala/edu/ie3/simona/agent/EnvironmentRefs.scala @@ -22,6 +22,8 @@ import org.apache.pekko.actor.{ActorRef => ClassicRef} * Reference to the primary service proxy * @param weather * Reference to the service, that provides weather information + * @param loadProfiles + * Reference to the service, that provides load profile information * @param evDataService * Reference to the EV data service, if existing */ @@ -30,5 +32,6 @@ final case class EnvironmentRefs( runtimeEventListener: ActorRef[RuntimeEvent], primaryServiceProxy: ClassicRef, weather: ClassicRef, + loadProfiles: ClassicRef, evDataService: Option[ClassicRef], ) diff --git a/src/main/scala/edu/ie3/simona/agent/grid/GridAgentController.scala b/src/main/scala/edu/ie3/simona/agent/grid/GridAgentController.scala index 8d615ec3af..86f873ae97 100644 --- a/src/main/scala/edu/ie3/simona/agent/grid/GridAgentController.scala +++ b/src/main/scala/edu/ie3/simona/agent/grid/GridAgentController.scala @@ -15,6 +15,7 @@ import edu.ie3.simona.agent.em.EmAgent import edu.ie3.simona.agent.participant.ParticipantAgent.ParticipantMessage import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService.{ ActorExtEvDataService, + ActorLoadProfileService, ActorWeatherService, } import edu.ie3.simona.agent.participant.evcs.EvcsAgent @@ -31,6 +32,7 @@ import edu.ie3.simona.event.ResultEvent import edu.ie3.simona.event.notifier.NotifierConfig import edu.ie3.simona.exceptions.CriticalFailureException import edu.ie3.simona.exceptions.agent.GridAgentInitializationException +import edu.ie3.simona.model.participant.load.LoadModelBehaviour import edu.ie3.simona.ontology.messages.SchedulerMessage.ScheduleActivation import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage.FlexResponse import edu.ie3.simona.util.ConfigUtil @@ -345,6 +347,7 @@ class GridAgentController( input.getUuid ), environmentRefs.primaryServiceProxy, + environmentRefs.loadProfiles, simulationStartDate, simulationEndDate, resolution, @@ -530,6 +533,7 @@ class GridAgentController( loadInput: LoadInput, modelConfiguration: LoadRuntimeConfig, primaryServiceProxy: ClassicRef, + loadProfileService: ClassicRef, simulationStartDate: ZonedDateTime, simulationEndDate: ZonedDateTime, resolution: Long, @@ -545,7 +549,7 @@ class GridAgentController( loadInput, modelConfiguration, primaryServiceProxy, - Iterable.empty, + Iterable(ActorLoadProfileService(loadProfileService)), simulationStartDate, simulationEndDate, resolution, diff --git a/src/main/scala/edu/ie3/simona/agent/participant/ServiceRegistration.scala b/src/main/scala/edu/ie3/simona/agent/participant/ServiceRegistration.scala index 5308b4e768..8b064bf548 100644 --- a/src/main/scala/edu/ie3/simona/agent/participant/ServiceRegistration.scala +++ b/src/main/scala/edu/ie3/simona/agent/participant/ServiceRegistration.scala @@ -7,24 +7,36 @@ package edu.ie3.simona.agent.participant import org.apache.pekko.actor.ActorRef -import edu.ie3.datamodel.models.input.system.{EvcsInput, SystemParticipantInput} +import edu.ie3.datamodel.models.input.system.{ + EvcsInput, + LoadInput, + SystemParticipantInput, +} +import edu.ie3.datamodel.models.profile.LoadProfile import edu.ie3.simona.agent.participant.data.Data.PrimaryData.PrimaryDataWithApparentPower import edu.ie3.simona.agent.participant.data.Data.SecondaryData import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService.{ ActorExtEvDataService, + ActorLoadProfileService, ActorPriceService, ActorWeatherService, } import edu.ie3.simona.agent.participant.statedata.ParticipantStateData import edu.ie3.simona.config.SimonaConfig import edu.ie3.simona.exceptions.agent.ServiceRegistrationException +import edu.ie3.simona.model.participant.load.{ + LoadModel, + ProfileLoadModel, + RandomLoadModel, +} import edu.ie3.simona.model.participant.{ CalcRelevantData, ModelState, SystemParticipant, } import edu.ie3.simona.ontology.messages.services.EvMessage.RegisterForEvDataMessage +import edu.ie3.simona.ontology.messages.services.LoadProfileMessage.RegisterForLoadProfileService import edu.ie3.simona.ontology.messages.services.WeatherMessage.RegisterForWeatherMessage trait ServiceRegistration[ @@ -74,20 +86,47 @@ trait ServiceRegistration[ serviceDefinition: SecondaryDataService[S], inputModel: I, ): Option[ActorRef] = serviceDefinition match { - case SecondaryDataService.ActorPriceService(_) => + case ActorPriceService(_) => log.debug( s"Attempt to register for {}. This is currently not supported.", ActorPriceService, ) None + case ActorLoadProfileService(serviceRef) => + registerForLoadProfile(serviceRef, inputModel) + Some(serviceRef) case ActorWeatherService(serviceRef) => registerForWeather(serviceRef, inputModel) Some(serviceRef) case ActorExtEvDataService(serviceRef) => registerForEvData(serviceRef, inputModel) Some(serviceRef) + case service: SecondaryDataService[S] => + log.debug( + s"Attempt to register for {}. This is currently not supported.", + service, + ) + None } + /** Register for the load profile service + * + * @param actorRef + * Actor reference of the weather service + * @param inputModel + * Input model of the simulation mode + * @return + */ + private def registerForLoadProfile(actorRef: ActorRef, inputModel: I): Unit = + inputModel match { + case load: LoadInput => + actorRef ! RegisterForLoadProfileService(load.getLoadProfile) + case other => + throw new ServiceRegistrationException( + s"Cannot register for load profile information for model $other, because only profile and random load model can register for this." + ) + } + /** Register for the weather service * * @param actorRef diff --git a/src/main/scala/edu/ie3/simona/agent/participant/data/secondary/SecondaryDataService.scala b/src/main/scala/edu/ie3/simona/agent/participant/data/secondary/SecondaryDataService.scala index ebac2ad136..67b816f4ca 100644 --- a/src/main/scala/edu/ie3/simona/agent/participant/data/secondary/SecondaryDataService.scala +++ b/src/main/scala/edu/ie3/simona/agent/participant/data/secondary/SecondaryDataService.scala @@ -11,6 +11,7 @@ import edu.ie3.simona.agent.participant.data.Data.SecondaryData import edu.ie3.simona.agent.participant.data.Data.SecondaryData.WholesalePrice import edu.ie3.simona.agent.participant.data.DataService import edu.ie3.simona.ontology.messages.services.EvMessage.EvData +import edu.ie3.simona.ontology.messages.services.LoadProfileMessage.LoadProfileData import edu.ie3.simona.ontology.messages.services.WeatherMessage.WeatherData /** Common properties to all secondary data services @@ -24,6 +25,9 @@ object SecondaryDataService { final case class ActorWeatherService(override val actorRef: ActorRef) extends SecondaryDataService[WeatherData] + final case class ActorLoadProfileService(override val actorRef: ActorRef) + extends SecondaryDataService[LoadProfileData] + final case class ActorExtEvDataService(override val actorRef: ActorRef) extends SecondaryDataService[EvData] } diff --git a/src/main/scala/edu/ie3/simona/agent/participant/load/LoadAgent.scala b/src/main/scala/edu/ie3/simona/agent/participant/load/LoadAgent.scala index 67607acbf1..ee93422e7d 100644 --- a/src/main/scala/edu/ie3/simona/agent/participant/load/LoadAgent.scala +++ b/src/main/scala/edu/ie3/simona/agent/participant/load/LoadAgent.scala @@ -9,6 +9,8 @@ package edu.ie3.simona.agent.participant.load import edu.ie3.datamodel.models.input.system.LoadInput import edu.ie3.simona.agent.participant.ParticipantAgent import edu.ie3.simona.agent.participant.data.Data.PrimaryData.ComplexPower +import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService +import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService.ActorLoadProfileService import edu.ie3.simona.agent.participant.load.LoadAgentFundamentals.{ FixedLoadAgentFundamentals, ProfileLoadAgentFundamentals, @@ -19,15 +21,9 @@ import edu.ie3.simona.agent.participant.statedata.ParticipantStateData.Participa import edu.ie3.simona.config.SimonaConfig.LoadRuntimeConfig import edu.ie3.simona.model.participant.CalcRelevantData.LoadRelevantData import edu.ie3.simona.model.participant.ModelState.ConstantState -import edu.ie3.simona.model.participant.load.profile.ProfileLoadModel -import edu.ie3.simona.model.participant.load.profile.ProfileLoadModel.ProfileRelevantData -import edu.ie3.simona.model.participant.load.random.RandomLoadModel -import edu.ie3.simona.model.participant.load.random.RandomLoadModel.RandomRelevantData -import edu.ie3.simona.model.participant.load.{ - FixedLoadModel, - LoadModel, - LoadModelBehaviour, -} +import edu.ie3.simona.model.participant.load.ProfileLoadModel.ProfileRelevantData +import edu.ie3.simona.model.participant.load.RandomLoadModel.RandomRelevantData +import edu.ie3.simona.model.participant.load._ import org.apache.pekko.actor.{ActorRef, Props} object LoadAgent { @@ -42,7 +38,13 @@ object LoadAgent { ): Props = LoadModelBehaviour(initStateData.modelConfig.modelBehaviour) match { case LoadModelBehaviour.FIX => - Props(new FixedLoadAgent(scheduler, initStateData, listener)) + Props( + new FixedLoadAgent( + scheduler, + initStateData.copy(secondaryDataServices = Iterable.empty), + listener, + ) + ) case LoadModelBehaviour.PROFILE => Props(new ProfileLoadAgent(scheduler, initStateData, listener)) case LoadModelBehaviour.RANDOM => @@ -79,7 +81,14 @@ object LoadAgent { ProfileRelevantData, ProfileLoadModel, ](scheduler, initStateData, listener) - with ProfileLoadAgentFundamentals + with ProfileLoadAgentFundamentals { + + override val neededServices: Vector[Class[_ <: SecondaryDataService[_]]] = + Vector( + classOf[ActorLoadProfileService] + ) + + } final class RandomLoadAgent( scheduler: ActorRef, @@ -93,7 +102,12 @@ object LoadAgent { RandomRelevantData, RandomLoadModel, ](scheduler, initStateData, listener) - with RandomLoadAgentFundamentals + with RandomLoadAgentFundamentals { + override val neededServices: Vector[Class[_ <: SecondaryDataService[_]]] = + Vector( + classOf[ActorLoadProfileService] + ) + } } /** Creating a load agent @@ -121,6 +135,8 @@ abstract class LoadAgent[LD <: LoadRelevantData, LM <: LoadModel[LD]]( LM, ](scheduler, initStateData) with LoadAgentFundamentals[LD, LM] { + + val neededServices: Vector[Class[_ <: SecondaryDataService[_]]] = Vector.empty /* * "Hey, SIMONA! What is handled in ParticipantAgent?" * "Hey, dude! The following things are handled in ParticipantAgent: diff --git a/src/main/scala/edu/ie3/simona/agent/participant/load/LoadAgentFundamentals.scala b/src/main/scala/edu/ie3/simona/agent/participant/load/LoadAgentFundamentals.scala index e01c463115..e43ee81675 100644 --- a/src/main/scala/edu/ie3/simona/agent/participant/load/LoadAgentFundamentals.scala +++ b/src/main/scala/edu/ie3/simona/agent/participant/load/LoadAgentFundamentals.scala @@ -31,32 +31,24 @@ import edu.ie3.simona.agent.state.AgentState import edu.ie3.simona.agent.state.AgentState.Idle import edu.ie3.simona.config.SimonaConfig.LoadRuntimeConfig import edu.ie3.simona.event.notifier.NotifierConfig -import edu.ie3.simona.exceptions.agent.InconsistentStateException +import edu.ie3.simona.exceptions.agent.{ + AgentInitializationException, + InconsistentStateException, +} import edu.ie3.simona.io.result.AccompaniedSimulationResult import edu.ie3.simona.model.SystemComponent import edu.ie3.simona.model.participant.CalcRelevantData.LoadRelevantData import edu.ie3.simona.model.participant.ModelState.ConstantState import edu.ie3.simona.model.participant.load.FixedLoadModel.FixedLoadRelevantData -import edu.ie3.simona.model.participant.load.profile.ProfileLoadModel.ProfileRelevantData -import edu.ie3.simona.model.participant.load.profile.{ - LoadProfileStore, - ProfileLoadModel, -} -import edu.ie3.simona.model.participant.load.random.RandomLoadModel.RandomRelevantData -import edu.ie3.simona.model.participant.load.random.{ - RandomLoadModel, - RandomLoadParamStore, -} -import edu.ie3.simona.model.participant.load.{ - FixedLoadModel, - LoadModel, - LoadReference, -} +import edu.ie3.simona.model.participant.load.ProfileLoadModel.ProfileRelevantData +import edu.ie3.simona.model.participant.load.RandomLoadModel.RandomRelevantData +import edu.ie3.simona.model.participant.load._ import edu.ie3.simona.model.participant.{FlexChangeIndicator, ModelState} import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage.{ FlexRequest, FlexResponse, } +import edu.ie3.simona.ontology.messages.services.LoadProfileMessage.LoadProfileData import edu.ie3.simona.util.SimonaConstants import edu.ie3.simona.util.TickUtil._ import edu.ie3.util.quantities.PowerSystemUnits.PU @@ -129,6 +121,12 @@ protected trait LoadAgentFundamentals[LD <: LoadRelevantData, LM <: LoadModel[ ConstantState.type, LM, ] = { + /* Check for needed services */ + if (!services.toSeq.map(_.getClass).containsSlice(neededServices)) + throw new AgentInitializationException( + s"LoadAgent cannot be initialized without a load profile service!" + ) + /* Build the calculation model */ val model = buildModel( @@ -156,20 +154,6 @@ protected trait LoadAgentFundamentals[LD <: LoadRelevantData, LM <: LoadModel[ fixedLoadModel.operationInterval.start, fixedLoadModel.operationInterval.end, ).filterNot(_ == lastTickInSimulation) - case profileLoadModel: ProfileLoadModel => - activationTicksInOperationTime( - simulationStartDate, - LoadProfileStore.resolution.getSeconds, - profileLoadModel.operationInterval.start, - profileLoadModel.operationInterval.end, - ) - case randomLoadModel: RandomLoadModel => - activationTicksInOperationTime( - simulationStartDate, - RandomLoadParamStore.resolution.getSeconds, - randomLoadModel.operationInterval.start, - randomLoadModel.operationInterval.end, - ) case _ => SortedSet.empty[Long] } @@ -201,6 +185,46 @@ protected trait LoadAgentFundamentals[LD <: LoadRelevantData, LM <: LoadModel[ ) } + /** @param baseStateData + * base state data + * @param currentTick + * current tick + * @return + * the [[LoadProfileData]] + */ + protected def retrieveLoadProfileData( + baseStateData: ParticipantModelBaseStateData[ + ComplexPower, + _ <: LoadRelevantData, + ConstantState.type, + LM, + ], + currentTick: Long, + ): LoadProfileData = { + // take the last load profile data, not necessarily the one for the current tick: + // we might receive flex control messages for irregular ticks + val (_, secondaryData) = baseStateData.receivedSecondaryDataStore + .last(currentTick) + .getOrElse( + throw new InconsistentStateException( + s"The model ${baseStateData.model} was not provided with any secondary data so far." + ) + ) + + /* extract load profile data from secondary data, which should have been requested and received before */ + secondaryData + .collectFirst { + // filter secondary data for weather data + case (_, data: LoadProfileData) => + data + } + .getOrElse( + throw new InconsistentStateException( + s"The model ${baseStateData.model} was not provided with needed weather data." + ) + ) + } + override def buildModel( inputModel: InputModelContainer[LoadInput], modelConfig: LoadRuntimeConfig, @@ -317,11 +341,31 @@ protected trait LoadAgentFundamentals[LD <: LoadRelevantData, LM <: LoadModel[ lastModelState: ConstantState.type, currentTick: Long, scheduler: ActorRef, - ): FSM.State[AgentState, ParticipantStateData[ComplexPower]] = - throw new InconsistentStateException( - s"Load model is not able to calculate power with secondary data." + ): FSM.State[AgentState, ParticipantStateData[ComplexPower]] = { + val voltage = + getAndCheckNodalVoltage(baseStateData, currentTick) + + val relevantData = + createCalcRelevantData( + baseStateData, + currentTick, + ) + + val result = baseStateData.model.calculatePower( + currentTick, + voltage, + ConstantState, + relevantData, ) + updateValueStoresInformListenersAndGoToIdleWithUpdatedBaseStateData( + scheduler, + baseStateData, + AccompaniedSimulationResult(result), + relevantData, + ) + } + /** Determine the average result within the given tick window * * @param tickToResults @@ -476,10 +520,14 @@ object LoadAgentFundamentals { ProfileLoadModel, ], currentTick: Long, - ): ProfileRelevantData = + ): ProfileRelevantData = { + val data = retrieveLoadProfileData(baseStateData, currentTick) + ProfileRelevantData( - currentTick.toDateTime(baseStateData.startDate) + data.averagePower, + data.maxPower, ) + } /** Partial function, that is able to transfer * [[ParticipantModelBaseStateData]] (holding the actual calculation model) @@ -536,10 +584,9 @@ object LoadAgentFundamentals { RandomLoadModel, ], tick: Long, - ): RandomRelevantData = - RandomRelevantData( - tick.toDateTime(baseStateData.startDate) - ) + ): RandomRelevantData = RandomRelevantData( + retrieveLoadProfileData(baseStateData, currentTick).averagePower + ) /** Partial function, that is able to transfer * [[ParticipantModelBaseStateData]] (holding the actual calculation model) diff --git a/src/main/scala/edu/ie3/simona/config/ConfigFailFast.scala b/src/main/scala/edu/ie3/simona/config/ConfigFailFast.scala index c3b16d2ca7..7627bdbbc7 100644 --- a/src/main/scala/edu/ie3/simona/config/ConfigFailFast.scala +++ b/src/main/scala/edu/ie3/simona/config/ConfigFailFast.scala @@ -137,6 +137,7 @@ object ConfigFailFast extends LazyLogging { checkPrimaryDataSource(simonaConfig.simona.input.primary) /* Check if the provided combination of data source and parameters are valid */ + checkLoadProfileDataSource(simonaConfig.simona.input.loadprofile.datasource) checkWeatherDataSource(simonaConfig.simona.input.weather.datasource) checkOutputConfig(simonaConfig.simona.output) @@ -558,6 +559,37 @@ object ConfigFailFast extends LazyLogging { ): Unit = PrimaryServiceProxy.checkConfig(primary) + private def checkLoadProfileDataSource( + loadProfileDataSourceCfg: SimonaConfig.Simona.Input.Loadprofile.Datasource + ): Unit = { + // check weather source parameters + val supportedLoadProfileSources = Set("csv", "sql") + val definedLoadProfileSources = Vector( + loadProfileDataSourceCfg.csvParams, + loadProfileDataSourceCfg.sqlParams, + ).filter(_.isDefined) + + // check that only one source is defined + if (definedLoadProfileSources.size > 1) + throw new InvalidConfigParameterException( + s"Multiple load profile sources defined: '${definedLoadProfileSources.map(_.getClass.getSimpleName).mkString("\n\t")}'." + + s"Please define only one source!\nAvailable sources:\n\t${supportedLoadProfileSources.mkString("\n\t")}" + ) + + definedLoadProfileSources.headOption.flatten match { + case Some(baseCsvParams: BaseCsvParams) => + checkBaseCsvParams(baseCsvParams, "LoadProfileSource") + case Some(params: SqlParams) => + checkSqlParams(params) + case Some(other) => + logger.warn( + s"The source '$other' is currently not supported. Therefore only the build in load profiles will be used!" + ) + case None => + logger.info("Using only the build in load profiles!") + } + } + private def checkWeatherDataSource( weatherDataSourceCfg: SimonaConfig.Simona.Input.Weather.Datasource ): Unit = { diff --git a/src/main/scala/edu/ie3/simona/config/SimonaConfig.scala b/src/main/scala/edu/ie3/simona/config/SimonaConfig.scala index 0790a6efd9..49ddd7f66d 100644 --- a/src/main/scala/edu/ie3/simona/config/SimonaConfig.scala +++ b/src/main/scala/edu/ie3/simona/config/SimonaConfig.scala @@ -1244,6 +1244,7 @@ object SimonaConfig { final case class Input( grid: SimonaConfig.Simona.Input.Grid, + loadprofile: SimonaConfig.Simona.Input.Loadprofile, primary: SimonaConfig.Simona.Input.Primary, weather: SimonaConfig.Simona.Input.Weather, ) @@ -1311,6 +1312,106 @@ object SimonaConfig { } } + final case class Loadprofile( + datasource: SimonaConfig.Simona.Input.Loadprofile.Datasource + ) + object Loadprofile { + final case class Datasource( + csvParams: scala.Option[SimonaConfig.BaseCsvParams], + sqlParams: scala.Option[ + SimonaConfig.Simona.Input.Loadprofile.Datasource.SqlParams + ], + ) + object Datasource { + final case class SqlParams( + jdbcUrl: java.lang.String, + password: java.lang.String, + schemaName: java.lang.String, + tableName: java.lang.String, + userName: java.lang.String, + ) + object SqlParams { + def apply( + c: com.typesafe.config.Config, + parentPath: java.lang.String, + $tsCfgValidator: $TsCfgValidator, + ): SimonaConfig.Simona.Input.Loadprofile.Datasource.SqlParams = { + SimonaConfig.Simona.Input.Loadprofile.Datasource.SqlParams( + jdbcUrl = $_reqStr(parentPath, c, "jdbcUrl", $tsCfgValidator), + password = $_reqStr(parentPath, c, "password", $tsCfgValidator), + schemaName = + if (c.hasPathOrNull("schemaName")) c.getString("schemaName") + else "public", + tableName = + $_reqStr(parentPath, c, "tableName", $tsCfgValidator), + userName = $_reqStr(parentPath, c, "userName", $tsCfgValidator), + ) + } + private def $_reqStr( + parentPath: java.lang.String, + c: com.typesafe.config.Config, + path: java.lang.String, + $tsCfgValidator: $TsCfgValidator, + ): java.lang.String = { + if (c == null) null + else + try c.getString(path) + catch { + case e: com.typesafe.config.ConfigException => + $tsCfgValidator.addBadPath(parentPath + path, e) + null + } + } + + } + + def apply( + c: com.typesafe.config.Config, + parentPath: java.lang.String, + $tsCfgValidator: $TsCfgValidator, + ): SimonaConfig.Simona.Input.Loadprofile.Datasource = { + SimonaConfig.Simona.Input.Loadprofile.Datasource( + csvParams = + if (c.hasPathOrNull("csvParams")) + scala.Some( + SimonaConfig.BaseCsvParams( + c.getConfig("csvParams"), + parentPath + "csvParams.", + $tsCfgValidator, + ) + ) + else None, + sqlParams = + if (c.hasPathOrNull("sqlParams")) + scala.Some( + SimonaConfig.Simona.Input.Loadprofile.Datasource.SqlParams( + c.getConfig("sqlParams"), + parentPath + "sqlParams.", + $tsCfgValidator, + ) + ) + else None, + ) + } + } + + def apply( + c: com.typesafe.config.Config, + parentPath: java.lang.String, + $tsCfgValidator: $TsCfgValidator, + ): SimonaConfig.Simona.Input.Loadprofile = { + SimonaConfig.Simona.Input.Loadprofile( + datasource = SimonaConfig.Simona.Input.Loadprofile.Datasource( + if (c.hasPathOrNull("datasource")) c.getConfig("datasource") + else + com.typesafe.config.ConfigFactory.parseString("datasource{}"), + parentPath + "datasource.", + $tsCfgValidator, + ) + ) + } + } + final case class Primary( couchbaseParams: scala.Option[ SimonaConfig.Simona.Input.Primary.CouchbaseParams @@ -1937,6 +2038,12 @@ object SimonaConfig { parentPath + "grid.", $tsCfgValidator, ), + loadprofile = SimonaConfig.Simona.Input.Loadprofile( + if (c.hasPathOrNull("loadprofile")) c.getConfig("loadprofile") + else com.typesafe.config.ConfigFactory.parseString("loadprofile{}"), + parentPath + "loadprofile.", + $tsCfgValidator, + ), primary = SimonaConfig.Simona.Input.Primary( if (c.hasPathOrNull("primary")) c.getConfig("primary") else com.typesafe.config.ConfigFactory.parseString("primary{}"), diff --git a/src/main/scala/edu/ie3/simona/model/participant/load/DayType.scala b/src/main/scala/edu/ie3/simona/model/participant/load/DayType.scala deleted file mode 100644 index 462bb03d1d..0000000000 --- a/src/main/scala/edu/ie3/simona/model/participant/load/DayType.scala +++ /dev/null @@ -1,46 +0,0 @@ -/* - * © 2020. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.simona.model.participant.load - -import java.time.DayOfWeek -import java.time.DayOfWeek.{SATURDAY, SUNDAY} - -/** Describes the type of day (weekday, saturday or sunday) as part of a load - * profile key - */ -object DayType extends Enumeration { - val weekday, saturday, sunday = Value - - /** Creates a day type from given Java DayOfWeek object - * @param dayOfWeek - * a DayOfWeek enum value - * @return - * a day type - */ - def apply(dayOfWeek: DayOfWeek): DayType.Value = dayOfWeek match { - case SATURDAY => saturday - case SUNDAY => sunday - case _ => weekday - } - - /** Creates a day type from given String key, taken from the csv or database - * table header - * @param key - * day type, such as "We" for weekday - * @return - * a day type - */ - def apply(key: String): DayType.Value = key match { - case "Wd" => weekday - case "Sa" => saturday - case "Su" => sunday - case _ => - throw new RuntimeException( - "Malformed header. '" + key + "' not found as DayType. Permissible keys: 'Wd', 'Sa', 'Su'" - ) - } -} diff --git a/src/main/scala/edu/ie3/simona/model/participant/load/profile/ProfileLoadModel.scala b/src/main/scala/edu/ie3/simona/model/participant/load/ProfileLoadModel.scala similarity index 77% rename from src/main/scala/edu/ie3/simona/model/participant/load/profile/ProfileLoadModel.scala rename to src/main/scala/edu/ie3/simona/model/participant/load/ProfileLoadModel.scala index e30390bb74..b1843c4ec0 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/load/profile/ProfileLoadModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/load/ProfileLoadModel.scala @@ -4,21 +4,21 @@ * Research group Distribution grid planning and operation */ -package edu.ie3.simona.model.participant.load.profile +package edu.ie3.simona.model.participant.load +import edu.ie3.datamodel.exceptions.SourceException import edu.ie3.datamodel.models.input.system.LoadInput import edu.ie3.datamodel.models.profile.StandardLoadProfile import edu.ie3.simona.model.participant.CalcRelevantData.LoadRelevantData import edu.ie3.simona.model.participant.ModelState.ConstantState import edu.ie3.simona.model.participant.control.QControl import edu.ie3.simona.model.participant.load.LoadReference._ -import edu.ie3.simona.model.participant.load.profile.ProfileLoadModel.ProfileRelevantData -import edu.ie3.simona.model.participant.load.{LoadModel, LoadReference} +import edu.ie3.simona.model.participant.load.ProfileLoadModel.ProfileRelevantData +import edu.ie3.simona.service.load.LoadProfileStore import edu.ie3.util.scala.OperationInterval import edu.ie3.util.scala.quantities.ApparentPower import squants.Power -import java.time.ZonedDateTime import java.util.UUID /** Power model consuming power according to standard load profiles @@ -58,16 +58,11 @@ final case class ProfileLoadModel( cosPhiRated, ) { - private val loadProfileStore: LoadProfileStore = LoadProfileStore() - - /* maximum energy throughout the year of the selected load profile*/ - private val profileMaxPower = loadProfileStore.maxPower(loadProfile) - /* energy reference is always models yearly energy consumption divided by the energy the profile is scaled to */ private lazy val energyReferenceScalingFactor = reference match { case EnergyConsumption(energyConsumption) => - energyConsumption / LoadProfileStore.defaultLoadProfileEnergyScaling + energyConsumption / LoadProfileStore.profileScaling(loadProfile) case _ => throw new IllegalArgumentException( s"Applying energy reference scaling factor for reference mode '$reference' is not supported!" @@ -85,26 +80,25 @@ final case class ProfileLoadModel( modelState: ConstantState.type, data: ProfileRelevantData, ): Power = { - /* The power comes in W and is delivered all 15 minutes */ - val averagePower: Power = loadProfileStore - .entry(data.date, loadProfile) - + /* The power comes in kW and is delivered all 15 minutes */ reference match { case ActivePower(activePower) => /* scale the reference active power based on the profiles averagePower/maxPower ratio */ - val referenceScalingFactor = averagePower / profileMaxPower + val referenceScalingFactor = data.averagePower / data.maxPower activePower * referenceScalingFactor case _: EnergyConsumption => - /* scale the profiles average power based on the energyConsumption/profileEnergyScaling(=1000kWh/year) ratio */ - averagePower * energyReferenceScalingFactor + /* scale the profiles average power based on the energyConsumption/profileEnergyScaling ratio */ + data.averagePower * energyReferenceScalingFactor } } } object ProfileLoadModel { - final case class ProfileRelevantData(date: ZonedDateTime) - extends LoadRelevantData + final case class ProfileRelevantData( + averagePower: Power, + maxPower: Power, + ) extends LoadRelevantData def apply( input: LoadInput, @@ -121,15 +115,23 @@ object ProfileLoadModel { LoadModel.scaleSRatedActivePower(scaledInput, power) case LoadReference.EnergyConsumption(energyConsumption) => - val loadProfileMax = - LoadProfileStore().maxPower( + val loadProfileMax = LoadProfileStore + .maxPower( scaledInput.getLoadProfile.asInstanceOf[StandardLoadProfile] ) + .getOrElse( + throw new SourceException( + s"Expected a maximal power value for this load profile: ${input.getLoadProfile}!" + ) + ) + LoadModel.scaleSRatedEnergy( scaledInput, energyConsumption, loadProfileMax, - LoadProfileStore.defaultLoadProfileEnergyScaling, + LoadProfileStore.profileScaling( + scaledInput.getLoadProfile.asInstanceOf[StandardLoadProfile] + ), ) } diff --git a/src/main/scala/edu/ie3/simona/model/participant/load/random/RandomLoadModel.scala b/src/main/scala/edu/ie3/simona/model/participant/load/RandomLoadModel.scala similarity index 58% rename from src/main/scala/edu/ie3/simona/model/participant/load/random/RandomLoadModel.scala rename to src/main/scala/edu/ie3/simona/model/participant/load/RandomLoadModel.scala index bb0d95a9c8..26d5a364ce 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/load/random/RandomLoadModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/load/RandomLoadModel.scala @@ -4,28 +4,20 @@ * Research group Distribution grid planning and operation */ -package edu.ie3.simona.model.participant.load.random +package edu.ie3.simona.model.participant.load -import de.lmu.ifi.dbs.elki.math.statistics.distribution.GeneralizedExtremeValueDistribution -import de.lmu.ifi.dbs.elki.utilities.random.RandomFactory import edu.ie3.datamodel.models.input.system.LoadInput import edu.ie3.simona.model.participant.CalcRelevantData.LoadRelevantData import edu.ie3.simona.model.participant.ModelState.ConstantState import edu.ie3.simona.model.participant.control.QControl import edu.ie3.simona.model.participant.load.LoadReference._ -import edu.ie3.simona.model.participant.load.random.RandomLoadModel.RandomRelevantData -import edu.ie3.simona.model.participant.load.{DayType, LoadModel, LoadReference} -import edu.ie3.util.TimeUtil +import edu.ie3.simona.model.participant.load.RandomLoadModel.RandomRelevantData import edu.ie3.util.scala.OperationInterval import edu.ie3.util.scala.quantities.ApparentPower import squants.Power -import squants.energy.{KilowattHours, Kilowatts, Watts} +import squants.energy.{KilowattHours, Watts} -import java.time.ZonedDateTime import java.util.UUID -import scala.annotation.tailrec -import scala.collection.mutable -import scala.util.Random /** A load model consuming energy followed by time resolved probability. The * referencing to rated active power maps the output's 95 % quantile to this @@ -72,12 +64,6 @@ final case class RandomLoadModel( ) } - private val randomLoadParamStore = RandomLoadParamStore() - - private type GevKey = (DayType.Value, Int) - private val gevStorage = - mutable.Map.empty[GevKey, GeneralizedExtremeValueDistribution] - /** Calculate the active power behaviour of the model * * @param data @@ -85,72 +71,28 @@ final case class RandomLoadModel( * @return * Active power */ - @tailrec override protected def calculateActivePower( modelState: ConstantState.type, data: RandomRelevantData, ): Power = { - val gev = getGevDistribution(data.date) - - /* Get a next random power (in kW) */ - val randomPower = gev.nextRandom() - if (randomPower < 0) - calculateActivePower(modelState, data) - else { - val profilePower = Kilowatts(randomPower) - val activePower = reference match { - case ActivePower(activePower) => - /* scale the reference active power based on the random profiles averagePower/maxPower ratio */ - val referenceScalingFactor = - profilePower / RandomLoadModel.randomMaxPower - activePower * referenceScalingFactor - case _: EnergyConsumption => - /* scale the profiles random power based on the energyConsumption/profileEnergyScaling(=1000kWh/year) ratio */ - profilePower * energyReferenceScalingFactor - } - activePower - } - } - - /** Get the needed generalized extreme value distribution from the store or - * instantiate a new one and put it to the store. - * - * @param dateTime - * Questioned date time - * @return - * The needed generalized extreme value distribution - */ - private def getGevDistribution( - dateTime: ZonedDateTime - ): GeneralizedExtremeValueDistribution = { - /* Determine identifying key for a distinct generalized extreme value distribution and look it up. If it is not - * available, yet, instantiate one. */ - val key: GevKey = ( - DayType(dateTime.getDayOfWeek), - TimeUtil.withDefaults.getQuarterHourOfDay(dateTime), - ) - gevStorage.get(key) match { - case Some(foundIt) => foundIt - case None => - /* Instantiate new gev distribution, put it to storage and return it */ - val randomFactory = RandomFactory.get(Random.nextLong()) - val gevParameters = randomLoadParamStore.parameters(dateTime) - val newGev = new GeneralizedExtremeValueDistribution( - gevParameters.my, - gevParameters.sigma, - gevParameters.k, - randomFactory, - ) - gevStorage += (key -> newGev) - newGev + val profilePower = data.power + val activePower = reference match { + case ActivePower(activePower) => + /* scale the reference active power based on the random profiles averagePower/maxPower ratio */ + val referenceScalingFactor = + profilePower / RandomLoadModel.randomMaxPower + activePower * referenceScalingFactor + case _: EnergyConsumption => + /* scale the profiles random power based on the energyConsumption/profileEnergyScaling(=1000kWh/year) ratio */ + profilePower * energyReferenceScalingFactor } + activePower } } object RandomLoadModel { - final case class RandomRelevantData(date: ZonedDateTime) - extends LoadRelevantData + final case class RandomRelevantData(power: Power) extends LoadRelevantData /** The profile energy scaling factor, the random profile is scaled to. * diff --git a/src/main/scala/edu/ie3/simona/model/participant/load/profile/LoadProfileKey.scala b/src/main/scala/edu/ie3/simona/model/participant/load/profile/LoadProfileKey.scala deleted file mode 100644 index af77c3fa55..0000000000 --- a/src/main/scala/edu/ie3/simona/model/participant/load/profile/LoadProfileKey.scala +++ /dev/null @@ -1,104 +0,0 @@ -/* - * © 2020. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.simona.model.participant.load.profile - -import java.time.ZonedDateTime -import edu.ie3.datamodel.exceptions.ParsingException -import edu.ie3.datamodel.models.profile.StandardLoadProfile -import edu.ie3.simona.model.participant.load -import edu.ie3.simona.model.participant.load.{DayType, profile} - -/** A key describing a load profile, consisting of consumer type, a season and a - * day type. Is used to store load profile values for a single type. - * - * @param standardLoadProfile - * a consumer type - * @param season - * a season - * @param dayType - * a day type - */ -final case class LoadProfileKey( - standardLoadProfile: StandardLoadProfile, - season: Season.Value, - dayType: DayType.Value, -) - -case object LoadProfileKey { - - /** Creates a load profile key from given csv header, i.e. "g0SSo" - * - * @param headerKey - * the header - * @return - * a load profile key - */ - def apply(headerKey: String): LoadProfileKey = { - val regex = "([a-z][0-9])([A-Z][a-z])([A-Z][a-z])".r - - headerKey match { - case regex(loadProfileKey, seasonKey, dayTypeKey) => - LoadProfileKey(loadProfileKey, seasonKey, dayTypeKey) - case _ => - throw new RuntimeException( - s"Provided load profile header key $headerKey is malformed. It has to be of the form ${regex.pattern} e.g. 'g0WiSu'." - ) - } - } - - /** Creates a load profile key from three Strings describing a load profile - * - * @param loadProfile - * Key describing the load profile - * @param season - * Key describing the season - * @param dayType - * Key describing the day type - * @return - * a load profile key - */ - def apply( - loadProfile: String, - season: String, - dayType: String, - ): LoadProfileKey = { - try { - new LoadProfileKey( - StandardLoadProfile.parse(loadProfile), - Season(season), - DayType(dayType), - ) - } catch { - case e: ParsingException => - throw new IllegalArgumentException( - s"Cannot parse '$loadProfile' to a now StandardLoadProfile.", - e, - ) - } - } - - /** Creates a load profile key from a consumer type value and a ZonedDateTime - * object - * - * @param loadProfile - * The standard load profile - * @param time - * The time - * @return - * a load profile key - */ - def apply( - loadProfile: StandardLoadProfile, - time: ZonedDateTime, - ): LoadProfileKey = { - new LoadProfileKey( - loadProfile, - profile.Season(time), - load.DayType(time.getDayOfWeek), - ) - } -} diff --git a/src/main/scala/edu/ie3/simona/model/participant/load/profile/LoadProfileStore.scala b/src/main/scala/edu/ie3/simona/model/participant/load/profile/LoadProfileStore.scala deleted file mode 100644 index 209d9349c8..0000000000 --- a/src/main/scala/edu/ie3/simona/model/participant/load/profile/LoadProfileStore.scala +++ /dev/null @@ -1,245 +0,0 @@ -/* - * © 2020. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.simona.model.participant.load.profile - -import breeze.numerics.round -import com.typesafe.scalalogging.LazyLogging -import edu.ie3.datamodel.models.profile.{ - BdewStandardLoadProfile, - StandardLoadProfile, -} -import edu.ie3.simona.model.participant.load.profile.LoadProfileStore.{ - initializeMaxConsumptionPerProfile, - initializeTypeDayValues, -} -import edu.ie3.simona.model.participant.load.{DayType, profile} -import org.apache.commons.csv.CSVFormat -import squants.Power -import squants.energy.{KilowattHours, Watts} - -import java.io.{InputStreamReader, Reader} -import java.time.{Duration, ZonedDateTime} -import java.util -import scala.jdk.CollectionConverters._ -import scala.math.pow - -// needs to be imported for max function -import scala.math.Ordering.Double.IeeeOrdering - -/** Storage for a collection of standard load profiles. It is assumed, that each - * entry is given in W - which especially holds true for the standard load - * profiles provided by the german Federal Association of the Energy and Water - * Industry (Bundesverband der Energie- und Wasserwirtschaft - bdew). - * - * Access via: LoadProfileStore() - */ -class LoadProfileStore private (val reader: Reader) { - private val profileMap: Map[LoadProfileKey, TypeDayProfile] = - initializeTypeDayValues(reader) - private val maxParamMap: Map[StandardLoadProfile, Double] = - initializeMaxConsumptionPerProfile( - profileMap - ) - - /** Returns the load profiles entry (average power consumption for the - * following quarter-hour) for given time and load profile. - * - * @param time - * the requested time - * @param loadProfile - * the requested load profile - * @return - * a load in W - */ - def entry( - time: ZonedDateTime, - loadProfile: StandardLoadProfile, - ): squants.Power = { - val key = LoadProfileKey(loadProfile, time) - profileMap.get(key) match { - case Some(typeDayValues) => - val quarterHourEnergy = typeDayValues.getQuarterHourEnergy(time) - val load = loadProfile match { - case BdewStandardLoadProfile.H0 => - /* For the residential average profile, a dynamization has to be taken into account */ - val t = time.getDayOfYear // leap years are ignored - LoadProfileStore.dynamization(quarterHourEnergy, t) - case _ => quarterHourEnergy - } - Watts(load) - case None => - throw new RuntimeException( - "Value for LoadProfileKey " + key.toString + " not found." - ) - } - } - - /** Returns the maximum average power consumption per quarter-hour for a given - * load profile, calculated over all seasons and weekday types of given load - * profile - * - * @param loadProfile - * the consumer type - * @return - * the maximum load in W - */ - def maxPower( - loadProfile: StandardLoadProfile - ): Power = { - maxParamMap.get(loadProfile) match { - case Some(value) => - Watts(value) - case None => - throw new RuntimeException( - "Max value for ConsumerType " + loadProfile.toString + " not found" - ) - } - } -} - -object LoadProfileStore extends LazyLogging { - val resolution: Duration = Duration.ofMinutes(15) - - /** Default value store, that uses information from a file - * 'standard_load_profiles.csv' placed in the resources folder of the project - * / jar - */ - private lazy val defaultStore = new LoadProfileStore(getDefaultReader) - - /** Default standard load profile energy scaling - */ - val defaultLoadProfileEnergyScaling: squants.Energy = KilowattHours(1000d) - - /** Default entry point to get the default implementation with the provided - * default standard load profiles - * - * @return - * Instance of [[LoadProfileStore]] with default load profiles - */ - def apply(): LoadProfileStore = defaultStore - - /** Default entry point to get an instance of [[LoadProfileStore]] with - * customized load profiles provided by a specific reader including the - * files. For the default implementation with the provided default standard - * load profiles use [[apply()]] above. - * - * @param reader - * the reader containing the information where the file with custom load - * profiles is located - * @return - * instance of [[LoadProfileStore]] with custom load profiles - */ - def apply(reader: Reader): LoadProfileStore = new LoadProfileStore(reader) - - /** Calculates the dynamization factor for given day of year. Cf. - * Anwendung der repräsentativen Lastprofile - Step by step page 19 - * - * @param load - * load value - * @param t - * day of year (1-366) - * @return - * dynamization factor - */ - private def dynamization(load: Double, t: Int): Double = { - val factor = (-3.92e-10 * pow(t, 4) + 3.2e-7 * pow(t, 3) - - 7.02e-5 * pow(t, 2) + 2.1e-3 * t + 1.24) - val rndFactor = round(factor * 1e4) / 1e4 // round to 4 decimal places - round(load * rndFactor * 1e1) / 1e1 // rounded to 1 decimal place - } - - /** Initializes all type day values by receiving values from provided reader. - * - * @param reader - * a reader that is providing load profile values from a CSV file - */ - def initializeTypeDayValues( - reader: Reader = getDefaultReader - ): Map[LoadProfileKey, TypeDayProfile] = { - val parser = CSVFormat.Builder - .create() - .setHeader() - .setSkipHeaderRecord(true) - .build() - .parse(reader) - // records list is an ArrayList - val records = parser.getRecords - - val headerKeys: util.List[String] = parser.getHeaderNames - - // skip last column "quarter-hour" - (for (i <- Range(0, headerKeys.size() - 1)) yield { - val headerKey = headerKeys.get(i) - val profileKey = LoadProfileKey(headerKey) - - val values: Array[Double] = - records.asScala.map(record => record.get(headerKey).toDouble).toArray - - profileKey -> TypeDayProfile(values) - }).toMap - } - - /** Initializes a mapping from standard load profile to highest occurring - * energy consumption throughout the whole year - * - * @param profileMap - * mapping from standard load profile keys to type day values - * @return - * maximum energy consumption within a year for each known standard load - * profile - */ - private def initializeMaxConsumptionPerProfile( - profileMap: Map[LoadProfileKey, TypeDayProfile] - ): Map[StandardLoadProfile, Double] = { - /* Get all standard load profiles, that have been put into the store */ - val knownLoadProfiles: Set[StandardLoadProfile] = - profileMap.keySet.map(key => key.standardLoadProfile) - - knownLoadProfiles - .flatMap(loadProfile => { - (loadProfile match { - case BdewStandardLoadProfile.H0 => - // max load for h0 is expected to be exclusively found in winter, - // thus we only search there. - DayType.values - .map(dayType => { - val key = - profile.LoadProfileKey(loadProfile, Season.winter, dayType) - // maximum dynamization factor is on day 366 (leap year) or day 365 (regular year). - // The difference between day 365 and day 366 is negligible, thus pick 366 - profileMap - .get(key) - .map(typeDay => dynamization(typeDay.getMaxValue, 366)) - .getOrElse(0d) - }) - .maxOption - case _ => - (for (season <- Season.values; dayType <- DayType.values) yield { - val key = profile.LoadProfileKey(loadProfile, season, dayType) - profileMap.get(key) match { - case Some(value) => Option(value.getMaxValue) - case None => None - } - }).flatten.maxOption - }).map(maxConsumption => loadProfile -> maxConsumption) - }) - .toMap - } - - /** @return - * A reader pointing to the default load profile location - */ - private def getDefaultReader: Reader = { - logger.info( - "Loading default load profile file 'standard_load_profiles.csv' from jar." - ) - new InputStreamReader( - this.getClass.getResourceAsStream("/load/standard_load_profiles.csv") - ) - } -} diff --git a/src/main/scala/edu/ie3/simona/model/participant/load/profile/Season.scala b/src/main/scala/edu/ie3/simona/model/participant/load/profile/Season.scala deleted file mode 100644 index 0d9bb6541c..0000000000 --- a/src/main/scala/edu/ie3/simona/model/participant/load/profile/Season.scala +++ /dev/null @@ -1,59 +0,0 @@ -/* - * © 2020. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.simona.model.participant.load.profile - -import java.time.Month._ -import java.time.ZonedDateTime - -/** Describes the season (winter, summer, transition) as part of a load profile - * key - */ -object Season extends Enumeration { - val winter, summer, transition = Value - - /** Creates a season from given time - * @param time - * the time - * @return - * a season - */ - def apply(time: ZonedDateTime): Season.Value = { - val day = time.getDayOfMonth - - // winter: 1.11.-20.03. - // summer: 15.05.-14.09. - // transition: 21.03.-14.05. and - // 15.09.-31.10. - // (VDEW handbook) - - time.getMonth match { - case NOVEMBER | DECEMBER | JANUARY | FEBRUARY => winter - case x if x == MARCH && day <= 20 => winter - case x if x == MAY && day >= 15 => summer - case JUNE | JULY | AUGUST => summer - case x if x == SEPTEMBER && day <= 14 => summer - case _ => transition - } - } - - /** Creates a season from given String key, taken from the csv or database - * table header - * @param key - * season, such as "winter" - * @return - * a season - */ - def apply(key: String): Season.Value = key match { - case "Wi" | "Winter" => winter - case "Su" | "Summer" => summer - case "Tr" | "Intermediate" => transition - case _ => - throw new RuntimeException( - "Malformed header, \"" + key + "\" not found as Season. Permissible keys: 'Wi', 'Winter', 'Su', 'Summer', 'Tr', 'Intermediate'" - ) - } -} diff --git a/src/main/scala/edu/ie3/simona/model/participant/load/profile/TypeDayProfile.scala b/src/main/scala/edu/ie3/simona/model/participant/load/profile/TypeDayProfile.scala deleted file mode 100644 index 22358772b1..0000000000 --- a/src/main/scala/edu/ie3/simona/model/participant/load/profile/TypeDayProfile.scala +++ /dev/null @@ -1,46 +0,0 @@ -/* - * © 2020. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.simona.model.participant.load.profile - -import java.time.ZonedDateTime - -import edu.ie3.util.TimeUtil - -// needs to be imported for max function -import scala.math.Ordering.Double.IeeeOrdering - -/** Stores a slice of load profile data, that comprises a whole day (96 quarter - * hours). The data describes a typical day, that can unequivocally be - * identified by a [[LoadProfileKey]]. - * - * @param values - * 96 quarter-hour values for this load profile, - */ -final case class TypeDayProfile(private val values: Array[Double]) { - if (values.length != 96) - throw new IllegalArgumentException( - "You may only instantiate type day parameters with 96 values." - ) - - /** Returns a value for given time. If time is not a 15-min step, it is - * rounded up to the next 15-min slice. - * - * @param time - * the time - * @return - * the load value - */ - def getQuarterHourEnergy(time: ZonedDateTime): Double = { - val quartH = TimeUtil.withDefaults.getQuarterHourOfDay(time) - values(quartH) - } - - /** @return - * the maximum value of this profile - */ - def getMaxValue: Double = values.maxOption.getOrElse(Double.PositiveInfinity) -} diff --git a/src/main/scala/edu/ie3/simona/model/participant/load/random/RandomLoadParamStore.scala b/src/main/scala/edu/ie3/simona/model/participant/load/random/RandomLoadParamStore.scala deleted file mode 100644 index 33fb57a38a..0000000000 --- a/src/main/scala/edu/ie3/simona/model/participant/load/random/RandomLoadParamStore.scala +++ /dev/null @@ -1,229 +0,0 @@ -/* - * © 2020. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.simona.model.participant.load.random - -import java.io.{InputStreamReader, Reader} -import java.time.{Duration, ZonedDateTime} -import com.typesafe.scalalogging.LazyLogging -import edu.ie3.simona.exceptions.FileIOException -import edu.ie3.simona.model.participant.load.DayType -import edu.ie3.simona.model.participant.load.random.RandomLoadParamStore.initializeDayTypeValues -import org.apache.commons.csv.{CSVFormat, CSVRecord} - -import scala.jdk.CollectionConverters._ - -/** Storage for a collection of random load parameters. - */ -final case class RandomLoadParamStore private (reader: Reader) { - private val parameterMap: Map[DayType.Value, TypeDayParameters] = - initializeDayTypeValues(reader) - - /** Returns the random load parameters for given time. - * - * @param time - * the requested time - * @return - * [[RandomLoadParameters]] to use for the given time - */ - def parameters(time: ZonedDateTime): RandomLoadParameters = { - val dayType = DayType(time.getDayOfWeek) - parameterMap - .getOrElse( - dayType, - throw new RuntimeException( - s"Cannot determine the random load parameters for '$time' (day type '$dayType')." - ), - ) - .getQuarterHourParameters(time) - } -} - -case object RandomLoadParamStore extends LazyLogging { - val resolution: Duration = Duration.ofMinutes(15) - - /** Default value store, that uses information from a file - * 'random_load_parameters.csv' placed in the resources folder of the project - * / jar - */ - private lazy val defaultStore = new RandomLoadParamStore(getDefaultReader) - - /** Default entry point to get the default implementation with the provided - * default random load parameters - * - * @return - * Instance of [[RandomLoadParamStore]] with default random load parameters - */ - def apply(): RandomLoadParamStore = defaultStore - - /** Default entry point to get an instance of [[RandomLoadParamStore]] with - * customized random load model parameters provided by a specific reader - * including the files. For the default implementation with the provided - * default random load parameters use [[apply()]] above. - * - * @param reader - * the reader containing the information where the file with custom random - * load parameters is located - * @return - * instance of [[RandomLoadParamStore]] with custom random load parameters - */ - def apply(reader: Reader): RandomLoadParamStore = - new RandomLoadParamStore(reader) - - /** Returns a [[CSVFormat]] with the first line as its header - */ - private def csvParser: CSVFormat = - CSVFormat.DEFAULT.builder().setHeader().setSkipHeaderRecord(true).build() - - /** Initializes all type day values by receiving values from provided reader. - * - * @param reader - * a reader that is providing random load parameters from a CSV file - */ - def initializeDayTypeValues( - reader: Reader - ): Map[DayType.Value, TypeDayParameters] = { - val parser = csvParser.parse(reader) - /* records list is an ArrayList */ - val records = parser.getRecords - - val headerElements = List.from(parser.getHeaderNames.asScala) - val descriptorTree = buildDescriptorTree(headerElements) - - /* Go through all lines of the csv file */ - records.asScala - .flatMap(record => { - val quartHour = record.get("quarterHour").toInt - - /* Go through all day types */ - descriptorTree.map { case (dayType, parameterToCol) => - try { - (dayType, quartHour, assembleParameters(record, parameterToCol)) - } catch { - case e: FileIOException => - throw new FileIOException( - s"Cannot determine random load parameters for day type '$dayType' and quarter-hour '$quartHour'", - e, - ) - } - } - }) - .groupMap { case (dayType, _, _) => dayType } { - case (_, quarterHour, randomLoadParameters) => - (quarterHour, randomLoadParameters) - } // Group entries by day type - .map { // For each day type, sort the parameters by quarter-hour and build a type day parameter object from it - case (dayType, quarterHourToParameters) => - dayType -> TypeDayParameters( - quarterHourToParameters.sortBy(_._1).map(_._2).toArray - ) - } - } - - /** Builds a descriptor tree, which gives information what to find where in - * the file. Each headline element breaks down to encoded information about - * the probability density function parameter and the addressed day type. All - * headline elements are treated this way and a mapping from day type to - * column position of parameter is build. - * - * @param headerElements - * List of headline elements - * @return - * Mapping from day type to a mapping from parameter to column index - */ - private def buildDescriptorTree( - headerElements: List[String] - ): Map[DayType.Value, Map[RandomLoadParameters.Value, Int]] = { - /* Each header entry encodes a pair of parameter and day type */ - type HeaderKey = (RandomLoadParameters.Value, DayType.Value) - - headerElements - .filterNot(_ == "quarterHour") - .zipWithIndex - .map { case (key, colIndex) => - /* Extract parameter and day type mapping from entry */ - val keyRegex = "(k|my|sigma)(\\w{2})".r - val headerKey: HeaderKey = key match { - case keyRegex(parameterKey, dayTypeKey) => - (RandomLoadParameters(parameterKey), DayType(dayTypeKey)) - case unsupportedKey => - throw new IllegalArgumentException( - s"Cannot extract parameter and day type from head line entry '$unsupportedKey'" - ) - } - (headerKey._2, headerKey._1, colIndex) - } - .groupMap { case (dayType, _, _) => - dayType - } { case (_, randomLoadParameterKey, colIndex) => - (randomLoadParameterKey, colIndex) - } - .map { case (dayType, parameterKeyToColIndex) => - dayType -> parameterKeyToColIndex.map { case (parameterKey, colIndex) => - parameterKey -> colIndex - }.toMap - } - } - - /** Assembles a triple of entries in a [[CSVRecord]] to consistent - * [[RandomLoadParameters]] - * - * @param record - * Csv record to receive data from - * @param parameterToCol - * Mapping from parameter to column index - * @return - * A consistent [[RandomLoadParameters]] object - */ - private def assembleParameters( - record: CSVRecord, - parameterToCol: Map[RandomLoadParameters.Value, Int], - ): RandomLoadParameters = { - val k = record - .get( - parameterToCol.getOrElse( - RandomLoadParameters.K, - throw new FileIOException( - s"Cannot determine column index for random load parameter ${RandomLoadParameters.K}." - ), - ) - ) - .toDouble - val my = record - .get( - parameterToCol.getOrElse( - RandomLoadParameters.MY, - throw new FileIOException( - s"Cannot determine column index for random load parameter ${RandomLoadParameters.MY}." - ), - ) - ) - .toDouble - val sigma = record - .get( - parameterToCol.getOrElse( - RandomLoadParameters.SIGMA, - throw new FileIOException( - s"Cannot determine column index for random load parameter ${RandomLoadParameters.SIGMA}." - ), - ) - ) - .toDouble - RandomLoadParameters(k, my, sigma) - } - - /** @return - * A reader pointing to the default random load parameter location - */ - private def getDefaultReader: Reader = { - logger.info( - "Loading default random load parameters file 'random_load_parameters.csv' from jar." - ) - new InputStreamReader( - this.getClass.getResourceAsStream("/load/random_load_parameters.csv") - ) - } -} diff --git a/src/main/scala/edu/ie3/simona/model/participant/load/random/RandomLoadParameters.scala b/src/main/scala/edu/ie3/simona/model/participant/load/random/RandomLoadParameters.scala deleted file mode 100644 index 29399a499b..0000000000 --- a/src/main/scala/edu/ie3/simona/model/participant/load/random/RandomLoadParameters.scala +++ /dev/null @@ -1,30 +0,0 @@ -/* - * © 2020. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.simona.model.participant.load.random - -import edu.ie3.simona.util.ParsableEnumeration - -/** A consistent set of parameters, that are needed to describe a 'generalized - * extreme value' probability density function. In general the GEV is described - * by the three parameters "location", "scale" and "shape". - * - * @param k - * Shape parameter - * @param my - * Location parameter - * @param sigma - * Scale parameter - */ -final case class RandomLoadParameters(k: Double, my: Double, sigma: Double) - -/** Enumeration to list all possible parameters - */ -case object RandomLoadParameters extends ParsableEnumeration { - val K: Value = Value("k") - val MY: Value = Value("my") - val SIGMA: Value = Value("sigma") -} diff --git a/src/main/scala/edu/ie3/simona/model/participant/load/random/TypeDayParameters.scala b/src/main/scala/edu/ie3/simona/model/participant/load/random/TypeDayParameters.scala deleted file mode 100644 index 57210c68b2..0000000000 --- a/src/main/scala/edu/ie3/simona/model/participant/load/random/TypeDayParameters.scala +++ /dev/null @@ -1,41 +0,0 @@ -/* - * © 2020. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.simona.model.participant.load.random - -import java.time.ZonedDateTime - -import edu.ie3.simona.model.participant.load.DayType -import edu.ie3.util.TimeUtil - -/** Stores a slice of random load parameters, that comprises a whole day (96 - * quarter-hours). The data describes a typical day, that can unequivocally be - * identified by its [[DayType]]. - * - * @param values - * 96 quarter-hour values for this day type - */ -final case class TypeDayParameters( - private val values: Array[RandomLoadParameters] -) { - if (values.length != 96) - throw new IllegalArgumentException( - s"You may only instantiate type day parameters with 96 values. Apparent: ${values.length}." - ) - - /** Returns a value for given time. If time is not a 15-min step, it is - * rounded up to the next 15-min slice. - * - * @param time - * the time - * @return - * the random load parameters - */ - def getQuarterHourParameters(time: ZonedDateTime): RandomLoadParameters = { - val quartHour = TimeUtil.withDefaults.getQuarterHourOfDay(time) - values(quartHour) - } -} diff --git a/src/main/scala/edu/ie3/simona/ontology/messages/services/LoadProfileMessage.scala b/src/main/scala/edu/ie3/simona/ontology/messages/services/LoadProfileMessage.scala new file mode 100644 index 0000000000..afb1f8f04a --- /dev/null +++ b/src/main/scala/edu/ie3/simona/ontology/messages/services/LoadProfileMessage.scala @@ -0,0 +1,65 @@ +/* + * © 2024. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.simona.ontology.messages.services + +import edu.ie3.datamodel.models.profile.LoadProfile +import edu.ie3.simona.agent.participant.data.Data.SecondaryData +import edu.ie3.simona.ontology.messages.services.ServiceMessage.{ + ProvisionMessage, + RequestServiceInformationMessage, + ServiceRegistrationMessage, +} +import org.apache.pekko.actor.ActorRef +import squants.Power + +sealed trait LoadProfileMessage + +/** Declares all messages sent and received by the load profile service and load + * profile data provided through these messages + */ +object LoadProfileMessage { + + /** Indicate the [[edu.ie3.simona.service.load.LoadProfileService]] that the + * requested agent wants to receive + * [[edu.ie3.datamodel.models.value.PValue]]s for the provided load profile + * @param loadProfile + * of the + * [[edu.ie3.datamodel.models.timeseries.repetitive.LoadProfileTimeSeries]] + */ + final case class RegisterForLoadProfileService( + loadProfile: LoadProfile + ) extends LoadProfileMessage + with ServiceRegistrationMessage + + /** Provide power value for the requested tick + * @param tick + * The tick, for which the data is requested for + * @param data + * Actual information + * @param nextDataTick + * Foreseen next tick, where data is available + */ + final case class ProvideLoadProfileValue( + override val tick: Long, + override val serviceRef: ActorRef, + override val data: LoadProfileData, + override val nextDataTick: Option[Long], + ) extends LoadProfileMessage + with ProvisionMessage[LoadProfileData] + + /** Container class for the load profile information at a certain point in + * time + * + * @param averagePower + * the average power for the current 15 minutes intervall + */ + final case class LoadProfileData( + averagePower: Power, + maxPower: Power, + ) extends SecondaryData + +} diff --git a/src/main/scala/edu/ie3/simona/ontology/messages/services/ServiceMessage.scala b/src/main/scala/edu/ie3/simona/ontology/messages/services/ServiceMessage.scala index d7444454fd..6406093f9e 100644 --- a/src/main/scala/edu/ie3/simona/ontology/messages/services/ServiceMessage.scala +++ b/src/main/scala/edu/ie3/simona/ontology/messages/services/ServiceMessage.scala @@ -19,6 +19,10 @@ sealed trait ServiceMessage object ServiceMessage { + trait RequestServiceInformationMessage extends ServiceMessage { + val ref: ActorRef + } + /** Message used to register for a service */ trait ServiceRegistrationMessage extends ServiceMessage diff --git a/src/main/scala/edu/ie3/simona/service/load/LoadProfileService.scala b/src/main/scala/edu/ie3/simona/service/load/LoadProfileService.scala new file mode 100644 index 0000000000..252db6b22b --- /dev/null +++ b/src/main/scala/edu/ie3/simona/service/load/LoadProfileService.scala @@ -0,0 +1,462 @@ +/* + * © 2024. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.simona.service.load + +import edu.ie3.datamodel.io.connectors.SqlConnector +import edu.ie3.datamodel.io.csv.CsvLoadProfileMetaInformation +import edu.ie3.datamodel.io.factory.timeseries.{ + BdewLoadProfileFactory, + LoadProfileFactory, + RandomLoadProfileFactory, +} +import edu.ie3.datamodel.io.naming.{DatabaseNamingStrategy, FileNamingStrategy} +import edu.ie3.datamodel.io.source.LoadProfileSource +import edu.ie3.datamodel.io.source.csv.{ + CsvDataSource, + CsvLoadProfileSource, + CsvTimeSeriesMetaInformationSource, +} +import edu.ie3.datamodel.io.source.sql.{ + SqlDataSource, + SqlLoadProfileSource, + SqlTimeSeriesMetaInformationSource, +} +import edu.ie3.datamodel.models.profile.LoadProfile.RandomLoadProfile +import edu.ie3.datamodel.models.profile.{BdewStandardLoadProfile, LoadProfile} +import edu.ie3.datamodel.models.timeseries.repetitive.LoadProfileTimeSeries +import edu.ie3.datamodel.models.value.load.LoadValues +import edu.ie3.simona.config.SimonaConfig +import edu.ie3.simona.config.SimonaConfig.BaseCsvParams +import edu.ie3.simona.config.SimonaConfig.Simona.Input.Loadprofile.Datasource.SqlParams +import edu.ie3.simona.exceptions.InitializationException +import edu.ie3.simona.exceptions.WeatherServiceException.InvalidRegistrationRequestException +import edu.ie3.simona.ontology.messages.services.LoadProfileMessage.{ + LoadProfileData, + ProvideLoadProfileValue, + RegisterForLoadProfileService, +} +import edu.ie3.simona.ontology.messages.services.ServiceMessage +import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResponseMessage.{ + RegistrationFailedMessage, + RegistrationSuccessfulMessage, +} +import edu.ie3.simona.service.ServiceStateData.{ + InitializeServiceStateData, + ServiceActivationBaseStateData, +} +import edu.ie3.simona.service.SimonaService +import edu.ie3.simona.service.load.LoadProfileService.{ + InitLoadProfileServiceStateData, + LoadProfileInitializedStateData, + initLoadProfileStore, +} +import edu.ie3.simona.util.TickUtil.{RichZonedDateTime, TickLong} +import edu.ie3.simona.util.{SimonaConstants, TickUtil} +import edu.ie3.util.scala.collection.immutable.SortedDistinctSeq +import org.apache.pekko.actor.{ActorContext, ActorRef, Props} + +import java.nio.file.Path +import java.time.{Duration, ZonedDateTime} +import scala.jdk.CollectionConverters.MapHasAsScala +import scala.util.{Failure, Success, Try} + +/** Load Profile Service is responsible to register other actors that require + * load profile information and provide load profile time series information + * when requested + */ +final case class LoadProfileService( + override val scheduler: ActorRef, + private implicit val simulationStartTime: ZonedDateTime, + simulationEnd: ZonedDateTime, + private implicit val resolution: Duration = Duration.ofMinutes(15), +) extends SimonaService[LoadProfileInitializedStateData](scheduler) { + + /** Initialize the concrete service implementation using the provided + * initialization data. This method should perform all heavyweight tasks + * before the actor becomes ready. The return values are a) the state data of + * the initialized service and b) optional triggers that should be send to + * the [[edu.ie3.simona.scheduler.Scheduler]] together with the completion + * message that is send in response to the trigger that is send to start the + * initialization process + * + * @param initServiceData + * the data that should be used for initialization + * @return + * the state data of this service and optional tick that should be included + * in the completion message + */ + override def init( + initServiceData: InitializeServiceStateData + ): Try[(LoadProfileInitializedStateData, Option[Long])] = + initServiceData match { + case InitLoadProfileServiceStateData(dataSource) => + val loadProfileStore = initLoadProfileStore(dataSource) + + /* What is the first tick to be triggered for? And what are further activation ticks */ + val (maybeNextTick, furtherActivationTicks) = SortedDistinctSeq( + TickUtil + .getTicksInBetween( + SimonaConstants.FIRST_TICK_IN_SIMULATION, + simulationEnd.toTick, + resolution.toSeconds, + ) + .toSeq + ).pop + + val initializedStateData = LoadProfileInitializedStateData( + loadProfileStore, + Map.empty, + activationTicks = furtherActivationTicks, + maybeNextActivationTick = maybeNextTick, + ) + + Success( + initializedStateData, + maybeNextTick, + ) + case invalidData => + Failure( + new InitializationException( + s"Provided init data '${invalidData.getClass.getSimpleName}' for load profile service are invalid!" + ) + ) + } + + /** Handle a request to register for information from this service + * + * @param registrationMessage + * registration message to handle + * @param serviceStateData + * current state data of the actor + * @return + * the service stata data that should be used in the next state (normally + * with updated values) + */ + override protected def handleRegistrationRequest( + registrationMessage: ServiceMessage.ServiceRegistrationMessage + )(implicit + serviceStateData: LoadProfileInitializedStateData + ): Try[LoadProfileInitializedStateData] = registrationMessage match { + case RegisterForLoadProfileService(loadProfile) => + Success(handleRegistrationRequest(sender(), loadProfile)) + case invalidMessage => + Failure( + InvalidRegistrationRequestException( + "Cannot register an agent for load profile service with registration " + + s"request message '${invalidMessage.getClass.getSimpleName}'!" + ) + ) + } + + /** Try to register the sending agent with its load profile for load profile + * value provision + * + * @param agentToBeRegistered + * the agent that wants to be registered + * @param loadProfile + * of the agent + * @param serviceStateData + * the current service state data of this service + * @return + * an updated state data of this service that contains registration + * information if the registration has been carried out successfully + */ + private def handleRegistrationRequest( + agentToBeRegistered: ActorRef, + loadProfile: LoadProfile, + )(implicit + serviceStateData: LoadProfileInitializedStateData + ): LoadProfileInitializedStateData = { + + serviceStateData.profileToRefs.get(loadProfile) match { + case None => + /* The load profile itself is not known yet. Try to figure out, which weather coordinates are relevant */ + serviceStateData.loadProfileStore.valueProvider.get(loadProfile) match { + case Some(_) => + // we can provide data for the agent + agentToBeRegistered ! RegistrationSuccessfulMessage( + self, + serviceStateData.maybeNextActivationTick, + ) + + serviceStateData.copy(profileToRefs = + serviceStateData.profileToRefs + (loadProfile -> Vector( + agentToBeRegistered + )) + ) + + case None => + // we cannot provide data for the agent + log.error( + s"Unable to obtain necessary information to register for load profile '${loadProfile.getKey}'." + ) + + sender() ! RegistrationFailedMessage(self) + serviceStateData + } + + case Some(actorRefs) if !actorRefs.contains(agentToBeRegistered) => + // load profile is already known (= we have data for it), but this actor is not registered yet + agentToBeRegistered ! RegistrationSuccessfulMessage( + self, + serviceStateData.maybeNextActivationTick, + ) + + serviceStateData.copy( + profileToRefs = + serviceStateData.profileToRefs + (loadProfile -> (actorRefs :+ agentToBeRegistered)) + ) + + case Some(actorRefs) if actorRefs.contains(agentToBeRegistered) => + // actor is already registered, do nothing + log.warning( + "Sending actor {} is already registered", + agentToBeRegistered, + ) + serviceStateData + + case _ => + // actor is not registered and we don't have data for it + // inform the agentToBeRegistered that the registration failed as we don't have data for it + agentToBeRegistered ! RegistrationFailedMessage(self) + serviceStateData + } + } + + /** Send out the information to all registered recipients + * + * @param tick + * current tick data should be announced for + * @param serviceStateData + * the current state data of this service + * @return + * the service stata data that should be used in the next state (normally + * with updated values) together with the completion message that is send + * in response to the trigger that was sent to start this announcement + */ + override protected def announceInformation(tick: Long)(implicit + serviceStateData: LoadProfileInitializedStateData, + ctx: ActorContext, + ): (LoadProfileInitializedStateData, Option[Long]) = { + + /* Pop the next activation tick and update the state data */ + val ( + maybeNextTick: Option[Long], + updatedStateData: LoadProfileInitializedStateData, + ) = { + val (nextTick, remainderTicks) = serviceStateData.activationTicks.pop + (nextTick, serviceStateData.copy(activationTicks = remainderTicks)) + } + + val time = tick.toDateTime(simulationStartTime) + val loadProfileStore = serviceStateData.loadProfileStore + + serviceStateData.profileToRefs.foreach { case (loadProfile, actorRefs) => + loadProfileStore.valueOptions(time, loadProfile) match { + case Some((averagePower, maxPower)) => + /* Sending the found value to the requester */ + actorRefs.foreach(recipient => + recipient ! ProvideLoadProfileValue( + tick, + self, + LoadProfileData(averagePower, maxPower), + maybeNextTick, + ) + ) + case None => + /* There is no data available in the source. */ + log.warning( + s"No power value found for load profile {} for time: {}", + loadProfile, + time, + ) + } + } + + (updatedStateData, maybeNextTick) + } +} + +object LoadProfileService { + + def props( + scheduler: ActorRef, + startDateTime: ZonedDateTime, + simulationEnd: ZonedDateTime, + ): Props = Props( + new LoadProfileService( + scheduler, + startDateTime, + simulationEnd, + ) + ) + + /** @param loadProfileStore + * that stores that contains all load profiles + * @param profileToRefs + * map: actor ref to [[LoadProfile]] + * @param maybeNextActivationTick + * the next tick, when this actor is triggered by scheduler + * @param activationTicks + * sorted set of ticks, that yet have been sent to the scheduler (w\o next + * tick) + */ + final case class LoadProfileInitializedStateData( + loadProfileStore: LoadProfileStore, + profileToRefs: Map[LoadProfile, Vector[ActorRef]] = Map.empty, + override val maybeNextActivationTick: Option[Long], + override val activationTicks: SortedDistinctSeq[Long], + ) extends ServiceActivationBaseStateData + + /** Load profile service state data used for initialization of the load + * profile sources + * @param sourceDefinition + * the definition of the sources to use + */ + final case class InitLoadProfileServiceStateData( + sourceDefinition: SimonaConfig.Simona.Input.Loadprofile.Datasource + ) extends InitializeServiceStateData + + /** Initializes the load profile sources. + * @param cfg + * configuration + * @return + * the option for the build in [[LoadProfileTimeSeries]] as well as a map: + * load profile to source + */ + def initLoadProfileStore( + cfg: SimonaConfig.Simona.Input.Loadprofile.Datasource + ): LoadProfileStore = { + val definedSources = Vector( + cfg.csvParams, + cfg.sqlParams, + ).find(_.isDefined).flatten + + val otherSources: Map[LoadProfile, LoadProfileSource[_, _]] = + definedSources match { + case Some(BaseCsvParams(csvSep, directoryPath, _)) => + // initializing a csv load profile source + readCsvSources(csvSep, Path.of(directoryPath)) + + case Some(sqlParams: SqlParams) => + // initializing a sql load profile source + val sqlConnector = new SqlConnector( + sqlParams.jdbcUrl, + sqlParams.userName, + sqlParams.password, + ) + + readSqlSources(sqlConnector, sqlParams.schemaName) + case _ => + Map.empty[LoadProfile, LoadProfileSource[_, _]] + } + + LoadProfileStore(otherSources) + } + + /** Method to read csv load profiles. + * @param csvSep + * separator of the source + * @param directoryPath + * the path of the source + * @return + * a map: load profile to load profile time series + */ + private def readCsvSources( + csvSep: String, + directoryPath: Path, + ): Map[LoadProfile, CsvLoadProfileSource[_, _]] = { + val source = + new CsvDataSource(csvSep, directoryPath, new FileNamingStrategy()) + + val metaInformation = new CsvTimeSeriesMetaInformationSource( + source + ).getLoadProfileMetaInformation.asScala.toMap + val classesAndFactories = getClassesAndFactories(metaInformation.keySet) + + metaInformation.map { + case (_, information: CsvLoadProfileMetaInformation) => + val (profile, entryClass, entryFactory) = + classesAndFactories(information.getProfile) + + val loadProfileSource = + new CsvLoadProfileSource[LoadProfile, LoadValues]( + source, + information, + entryClass.asInstanceOf, + entryFactory.asInstanceOf, + ) + + profile -> loadProfileSource + } + } + + /** Method to read csv load profiles. + * @param sqlConnector + * to connect to a database + * @param schemaName + * of the load profile schema + * @return + * a map: load profile to load profile time series + */ + private def readSqlSources( + sqlConnector: SqlConnector, + schemaName: String, + ): Map[LoadProfile, SqlLoadProfileSource[_, _]] = { + val namingStrategy = new DatabaseNamingStrategy() + val sqlDataSource: SqlDataSource = + new SqlDataSource(sqlConnector, schemaName, namingStrategy) + + val metaInformation = new SqlTimeSeriesMetaInformationSource( + sqlConnector, + schemaName, + namingStrategy, + ).getLoadProfileMetaInformation.asScala.toMap + val classesAndFactories = getClassesAndFactories(metaInformation.keySet) + + metaInformation.map { case (_, information) => + val (profile, entryClass, entryFactory) = + classesAndFactories(information.getProfile) + + val loadProfileSource = + new SqlLoadProfileSource[LoadProfile, LoadValues]( + sqlDataSource, + information, + entryClass.asInstanceOf, + entryFactory.asInstanceOf, + ) + + profile -> loadProfileSource + } + } + + private def getClassesAndFactories( + profiles: Set[String] + ): Map[String, (LoadProfile, Class[_], LoadProfileFactory[_, _])] = { + profiles.map { + case profile @ ("G0" | "G1" | "G2" | "G3" | "G4" | "G5" | "G6" | "H0" | + "I0" | "I1" | "I2") => + val factory = new BdewLoadProfileFactory() + + profile -> ( + factory.parseProfile(profile), + classOf[BdewStandardLoadProfile], + factory + ) + case profile @ "random" => + profile -> ( + RandomLoadProfile.RANDOM_LOAD_PROFILE, + classOf[RandomLoadProfile], + new RandomLoadProfileFactory() + ) + case other => + throw new InitializationException( + s"No implementation found for load profile $other!" + ) + } + }.toMap +} diff --git a/src/main/scala/edu/ie3/simona/service/load/LoadProfileStore.scala b/src/main/scala/edu/ie3/simona/service/load/LoadProfileStore.scala new file mode 100644 index 0000000000..4dbcdea4b3 --- /dev/null +++ b/src/main/scala/edu/ie3/simona/service/load/LoadProfileStore.scala @@ -0,0 +1,198 @@ +/* + * © 2024. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.simona.service.load + +import edu.ie3.datamodel.io.source.LoadProfileSource +import edu.ie3.datamodel.models.profile.LoadProfile +import edu.ie3.simona.service.load.LoadProfileStore.{ + AveragePower, + MaxPower, + ValueProvider, +} +import edu.ie3.util.quantities.PowerSystemUnits +import squants.energy.{KilowattHours, Kilowatts, WattHours} +import tech.units.indriya.ComparableQuantity + +import java.time.ZonedDateTime +import java.util.Optional +import javax.measure.quantity.{Energy, Power} +import scala.collection.mutable +import scala.jdk.CollectionConverters.MapHasAsScala +import scala.jdk.OptionConverters.RichOptional + +/** Container class that stores all loaded load profiles. + * @param profileToSource + * map: [[LoadProfile]] to [[LoadProfileSource]] + * @param valueProvider + * map: [[LoadProfile]] to provider function + */ +final case class LoadProfileStore( + profileToSource: Map[LoadProfile, LoadProfileSource[_, _]], + maxPower: Map[LoadProfile, Option[MaxPower]], + profileEnergyScaling: Map[LoadProfile, squants.Energy], + private[load] val valueProvider: Map[LoadProfile, ValueProvider], +) { + + def getKnownProfiles: Set[LoadProfile] = valueProvider.keySet + + /** Returns the average and the max power options. + * @param time + * the requested time + * @param loadProfile + * the requested load profile + * @return + */ + def valueOptions( + time: ZonedDateTime, + loadProfile: LoadProfile, + ): Option[(AveragePower, MaxPower)] = + entry(time, loadProfile).zip(maxPower(loadProfile)) + + /** Returns the load profiles entry (average power consumption for the + * following quarter hour) for given time and load profile. + * + * @param time + * the requested time + * @param loadProfile + * the requested load profile + * @return + * a load in kW + */ + def entry( + time: ZonedDateTime, + loadProfile: LoadProfile, + ): Option[AveragePower] = + valueProvider.get(loadProfile).map(_.apply(time)) match { + case Some(value) => value + case None => + throw new RuntimeException( + s"Value for LoadProfile ${loadProfile.toString} and time $time not found." + ) + } +} + +object LoadProfileStore { + type AveragePower = squants.Power + type MaxPower = squants.Power + private type ValueProvider = ZonedDateTime => Option[AveragePower] + + /** Default standard load profile energy scaling */ + private val defaultLoadProfileEnergyScaling: squants.Energy = KilowattHours( + 1000d + ) + + private val maxPowerMap: mutable.Map[LoadProfile, Option[MaxPower]] = + mutable.Map() + + private val profileScalingMap: mutable.Map[LoadProfile, squants.Energy] = + mutable.Map() + + def apply( + sources: Map[LoadProfile, LoadProfileSource[_, _]] + ): LoadProfileStore = { + maxPowerMap.clear() + profileScalingMap.clear() + + val profileToSource = LoadProfileSource.getBdewLoadProfiles.asScala ++ Map( + LoadProfile.RandomLoadProfile.RANDOM_LOAD_PROFILE -> LoadProfileSource.getRandomLoadProfile + ) ++ sources + + val valueProvider: mutable.Map[LoadProfile, ValueProvider] = mutable.Map() + + // adds all other load profiles to the map + profileToSource.foreach { case (profile, lpts) => + valueProvider.put( + profile, + time => convert(lpts.getValue(time).flatMap(_.getP)), + ) + maxPowerMap.put(profile, convert(lpts.getMaxPower)) + lpts.getLoadProfileEnergyScaling.map(scaling => + profileScalingMap.put(profile, convert(scaling)) + ) + } + + LoadProfileStore( + profileToSource.toMap, + maxPowerMap.toMap, + profileScalingMap.toMap, + valueProvider.toMap, + ) + } + + def apply(): LoadProfileStore = LoadProfileStore(Map.empty) + + /** Returns the profile energy scaling for the given load profile. + * + * @param loadProfile + * the given profile + * @return + * the scaling + */ + def profileScaling(loadProfile: LoadProfile): squants.Energy = + profileScalingMap.getOrElse(loadProfile, defaultLoadProfileEnergyScaling) + + /** Returns the maximum average power consumption per quarter hour for a given + * load profile, calculated over all seasons and weekday types of given load + * profile + * + * @param loadProfile + * the consumer type + * @return + * the maximum load in kW + */ + def maxPower(loadProfile: LoadProfile): Option[MaxPower] = + maxPowerMap.get(loadProfile).flatten + + /** Method for scaling the provided power value. + * @param power + * given power + * @param loadProfileEnergyScaling + * scaling factor used by the source + * @return + * the scaled power value + */ + def scale( + power: squants.Power, + loadProfileEnergyScaling: squants.Energy, + )(implicit tolerance: squants.Energy = WattHours(1e-3)): squants.Power = if ( + defaultLoadProfileEnergyScaling ~= loadProfileEnergyScaling + ) { + power + } else { + power / loadProfileEnergyScaling * defaultLoadProfileEnergyScaling + } + + /** Converts an optional [[ComparableQuantity]] power to an option for + * [[Power]]. + * @param power + * that should be converted + * @return + * an option for [[squants.Power]] + */ + private def convert( + power: Optional[ComparableQuantity[Power]] + ): Option[squants.Power] = + power + .map(p => + Kilowatts(p.to(PowerSystemUnits.KILOWATT).getValue.doubleValue()) + ) + .toScala + + /** Converts an optional [[ComparableQuantity]] power to an option for + * [[Energy]]. + * @param energy + * that should be converted + * @return + * an option for [[squants.Energy]] + */ + private def convert( + energy: ComparableQuantity[Energy] + ): squants.Energy = + KilowattHours( + energy.to(PowerSystemUnits.KILOWATTHOUR).getValue.doubleValue() + ) +} diff --git a/src/main/scala/edu/ie3/simona/sim/SimonaSim.scala b/src/main/scala/edu/ie3/simona/sim/SimonaSim.scala index c8196aaf30..3d7dcd9583 100644 --- a/src/main/scala/edu/ie3/simona/sim/SimonaSim.scala +++ b/src/main/scala/edu/ie3/simona/sim/SimonaSim.scala @@ -91,11 +91,16 @@ object SimonaSim { val weatherService = simonaSetup.weatherService(ctx, scheduler) + // load profile service + val loadProfileService = + simonaSetup.loadProfileService(ctx, scheduler) + val environmentRefs = EnvironmentRefs( scheduler, runtimeEventListener.toClassic, primaryServiceProxy, weatherService, + loadProfileService, extSimulationData.evDataService, ) @@ -111,6 +116,7 @@ object SimonaSim { scheduler, primaryServiceProxy.toTyped, weatherService.toTyped, + loadProfileService.toTyped, ) ++ gridAgents ++ extSimulationData.extDataServices.values.map(_.toTyped) diff --git a/src/main/scala/edu/ie3/simona/sim/setup/SimonaSetup.scala b/src/main/scala/edu/ie3/simona/sim/setup/SimonaSetup.scala index 4c452a4c3c..0cc30f613e 100644 --- a/src/main/scala/edu/ie3/simona/sim/setup/SimonaSetup.scala +++ b/src/main/scala/edu/ie3/simona/sim/setup/SimonaSetup.scala @@ -97,6 +97,20 @@ trait SimonaSetup { scheduler: ActorRef[SchedulerMessage], ): ClassicRef + /** Creates a load profile service + * @param context + * actor context + * @param scheduler + * Actor reference to it's according scheduler to use + * @return + * An actor reference to the service as well as matching data to initialize + * the service + */ + def loadProfileService( + context: ActorContext[_], + scheduler: ActorRef[SchedulerMessage], + ): ClassicRef + /** Loads external simulations and provides corresponding actors and init data * * @param context diff --git a/src/main/scala/edu/ie3/simona/sim/setup/SimonaStandaloneSetup.scala b/src/main/scala/edu/ie3/simona/sim/setup/SimonaStandaloneSetup.scala index 55848d2ced..6fe8c0fb24 100644 --- a/src/main/scala/edu/ie3/simona/sim/setup/SimonaStandaloneSetup.scala +++ b/src/main/scala/edu/ie3/simona/sim/setup/SimonaStandaloneSetup.scala @@ -32,6 +32,8 @@ import edu.ie3.simona.scheduler.{ScheduleLock, Scheduler, TimeAdvancer} import edu.ie3.simona.service.SimonaService import edu.ie3.simona.service.ev.ExtEvDataService import edu.ie3.simona.service.ev.ExtEvDataService.InitExtEvData +import edu.ie3.simona.service.load.LoadProfileService +import edu.ie3.simona.service.load.LoadProfileService.InitLoadProfileServiceStateData import edu.ie3.simona.service.primary.PrimaryServiceProxy import edu.ie3.simona.service.primary.PrimaryServiceProxy.InitPrimaryServiceProxyStateData import edu.ie3.simona.service.weather.WeatherService @@ -193,6 +195,32 @@ class SimonaStandaloneSetup( weatherService } + override def loadProfileService( + context: ActorContext[_], + scheduler: ActorRef[SchedulerMessage], + ): ClassicRef = { + val loadProfileService = context.toClassic.simonaActorOf( + LoadProfileService.props( + scheduler.toClassic, + TimeUtil.withDefaults.toZonedDateTime( + simonaConfig.simona.time.startDateTime + ), + TimeUtil.withDefaults + .toZonedDateTime(simonaConfig.simona.time.endDateTime), + ), + "loadProfileAgent", + ) + + loadProfileService ! SimonaService.Create( + InitLoadProfileServiceStateData( + simonaConfig.simona.input.loadprofile.datasource + ), + ScheduleLock.singleKey(context, scheduler, INIT_SIM_TICK), + ) + + loadProfileService + } + override def extSimulations( context: ActorContext[_], scheduler: ActorRef[SchedulerMessage], diff --git a/src/test/resources/edu/ie3/simona/model/participant/load/random_load_parameters_test.csv b/src/test/resources/edu/ie3/simona/model/participant/load/random_load_parameters_test.csv deleted file mode 100644 index 5a90edd42b..0000000000 --- a/src/test/resources/edu/ie3/simona/model/participant/load/random_load_parameters_test.csv +++ /dev/null @@ -1,97 +0,0 @@ -kSa,kSu,kWd,mySa,mySu,myWd,sigmaSa,sigmaSu,sigmaWd,quarterHour -0.266806721687317,0.295997023582459,0.279087692499161,0.0610353946685791,0.0630703344941139,0.053140863776207,0.0357091873884201,0.0370676517486572,0.0293692331761122,0 -0.281179457902908,0.299608528614044,0.275292456150055,0.0560021996498108,0.058424074202776,0.0498424917459488,0.0319067053496838,0.0334825366735458,0.0265011098235846,1 -0.275563269853592,0.29670587182045,0.252942383289337,0.0528385005891323,0.0547995530068874,0.0472154095768929,0.0286294519901276,0.0310499873012304,0.0245211906731129,2 -0.268669873476028,0.278122246265411,0.222294941544533,0.0497889705002308,0.0522574931383133,0.0455464050173759,0.0267868731170893,0.0290201343595982,0.0230409931391478,3 -0.254242599010468,0.261944204568863,0.198370113968849,0.0480474047362804,0.0499211102724075,0.0444160588085651,0.025010583922267,0.0268981643021107,0.0216859001666307,4 -0.253653794527054,0.259219467639923,0.183828011155128,0.0465105324983597,0.0482752695679665,0.0433471724390984,0.0237453784793615,0.0254230201244354,0.0208296440541744,5 -0.232547923922539,0.22350800037384,0.151045009493828,0.0453270114958286,0.047629676759243,0.042874664068222,0.0228410512208939,0.0238813851028681,0.0201559588313103,6 -0.203599318861961,0.210258424282074,0.134497836232185,0.0445506721735001,0.0461143814027309,0.0425662696361542,0.0219020489603281,0.0230216048657894,0.0196861978620291,7 -0.161462053656578,0.199696600437164,0.119271591305733,0.0445012860000134,0.0455127842724323,0.0422079116106033,0.0207479763776064,0.0222345236688852,0.019109807908535,8 -0.132398754358292,0.181738555431366,0.0919454470276833,0.0439879409968853,0.0447122938930988,0.0418674796819687,0.0208074823021889,0.0220435112714767,0.0189618747681379,9 -0.141700059175491,0.172467365860939,0.089320495724678,0.0432550236582756,0.044278547167778,0.04146458953619,0.0204773582518101,0.0210072007030249,0.0188128501176834,10 -0.13490092754364,0.168581023812294,0.0863394215703011,0.0434850119054317,0.0437624379992485,0.0414730608463287,0.0204034727066755,0.0206510350108147,0.0187746584415436,11 -0.12891773879528,0.141391009092331,0.0815763771533966,0.0438057892024517,0.0438031405210495,0.0417981892824173,0.0202947575598955,0.020810017362237,0.0187485739588737,12 -0.0865642353892326,0.133543252944946,0.0992388054728508,0.0434134155511856,0.0436179116368294,0.0411902405321598,0.0195998474955559,0.0202769655734301,0.0186050590127707,13 -0.107326254248619,0.131506875157356,0.092495284974575,0.0422008074820042,0.0430818535387516,0.0411793626844883,0.0194479618221521,0.0201587229967117,0.0184435490518808,14 -0.12365210801363,0.138605788350105,0.0902709886431694,0.0423581749200821,0.0430288463830948,0.0415664203464985,0.0197580717504025,0.0195529088377953,0.018659807741642,15 -0.133885741233826,0.116305366158485,0.0866493508219719,0.0421508848667145,0.0430956780910492,0.0417333245277405,0.0191863905638456,0.0196987725794315,0.018691623583436,16 -0.114493139088154,0.109669730067253,0.11562417447567,0.0423808842897415,0.0430857576429844,0.0414095669984818,0.0193791724741459,0.0192934311926365,0.0187153313308954,17 -0.106474287807941,0.107617124915123,0.141828700900078,0.041958749294281,0.0426791086792946,0.0417547412216663,0.0191276986151934,0.0195350497961044,0.0195536445826292,18 -0.111373528838158,0.0949468687176705,0.143573239445686,0.0423438511788845,0.0426821634173393,0.0423176772892475,0.0193268302828074,0.0194729138165712,0.0196764413267374,19 -0.129695281386375,0.111747220158577,0.146539300680161,0.042476549744606,0.0431345589458942,0.0430354326963425,0.0200043357908726,0.0198324266821146,0.0201929099857807,20 -0.124765977263451,0.126777052879333,0.181237012147903,0.0424391217529774,0.0431524105370045,0.0431922376155853,0.0196738373488188,0.0196389146149158,0.0208003968000412,21 -0.166463151574135,0.128424167633057,0.222479611635208,0.0426704697310925,0.0434540957212448,0.0447872504591942,0.020222594961524,0.0205539185553789,0.0225220061838627,22 -0.15489549934864,0.121535487473011,0.279077500104904,0.0428037717938423,0.0438099093735218,0.0461413823068142,0.0201477259397507,0.0211283396929502,0.0241765789687634,23 -0.149270266294479,0.145163252949715,0.314658939838409,0.0454631112515926,0.0458601377904415,0.0499203614890575,0.0224028695374727,0.0220234617590904,0.0277903340756893,24 -0.190895333886147,0.153644308447838,0.411636203527451,0.0459029302000999,0.0457030460238457,0.0538441687822342,0.0229767691344023,0.0218722112476826,0.0328027196228504,25 -0.191453665494919,0.150224968791008,0.423471629619598,0.0472969971597195,0.047640148550272,0.0605787634849548,0.0232080388814211,0.0226516369730234,0.0382906869053841,26 -0.224633768200874,0.169333130121231,0.409934967756271,0.0484098829329014,0.0481277145445347,0.0650452002882957,0.0246889032423496,0.0234444178640842,0.0414291992783546,27 -0.23322768509388,0.195868730545044,0.387721389532089,0.0501904785633087,0.0495214238762856,0.0688034743070602,0.0260085947811604,0.0247648935765028,0.0444995686411858,28 -0.280531793832779,0.242264837026596,0.378016442060471,0.0515176840126514,0.0500866919755936,0.0698312446475029,0.0280240289866924,0.0263900514692068,0.044551245868206,29 -0.332535684108734,0.286791861057281,0.414923667907715,0.0538357272744179,0.0514319129288197,0.0683940351009369,0.0305218864232302,0.0274843797087669,0.0439568608999252,30 -0.356295973062515,0.335564076900482,0.428863227367401,0.055597260594368,0.0545664429664612,0.0673834607005119,0.0327046699821949,0.0305836349725723,0.0435638092458248,31 -0.381627827882767,0.356647431850433,0.41985896229744,0.0583727583289146,0.0575905330479145,0.066506527364254,0.0358871892094612,0.0332306325435638,0.042742770165205,32 -0.427426189184189,0.375110238790512,0.42010372877121,0.0632952600717545,0.0606768690049648,0.0665018707513809,0.0411307103931904,0.0364755131304264,0.0432239063084126,33 -0.418004125356674,0.411366045475006,0.413253307342529,0.0678070187568665,0.0630642250180244,0.0660132020711899,0.0440355911850929,0.0392848961055279,0.0416399873793125,34 -0.480116784572601,0.443256080150604,0.430845767259598,0.0709096193313599,0.0660979822278023,0.0664969906210899,0.0474613010883331,0.0422324053943157,0.0427436977624893,35 -0.492366999387741,0.433596074581146,0.438405454158783,0.0713961571455002,0.0694489479064941,0.0661367774009705,0.0493352487683296,0.0453926883637905,0.0428525730967522,36 -0.488669961690903,0.450059473514557,0.43893027305603,0.0731654316186905,0.0709325894713402,0.0653173625469208,0.050234030932188,0.0470801331102848,0.0422966778278351,37 -0.476427853107452,0.453100442886353,0.445331513881683,0.0735458433628082,0.0721306875348091,0.0635278075933456,0.0500484444200993,0.048948809504509,0.0411276556551456,38 -0.447788894176483,0.462226986885071,0.42346379160881,0.0715678483247757,0.0739922747015953,0.0632445514202118,0.0481005422770977,0.0500194020569324,0.0407665930688381,39 -0.448646247386932,0.481568813323975,0.437915831804276,0.0719510018825531,0.0743175819516182,0.0634156614542007,0.0479790642857552,0.0502395480871201,0.0411010235548019,40 -0.45268851518631,0.463462620973587,0.44245383143425,0.0715559348464012,0.0745281055569649,0.0629907771945,0.0486291088163853,0.0509366057813168,0.0407464392483234,41 -0.472129553556442,0.458590388298035,0.438809961080551,0.0714246481657028,0.0753235965967178,0.0624452345073223,0.0488574244081974,0.0513948574662209,0.0403173267841339,42 -0.465200632810593,0.482033789157867,0.437554955482483,0.07085170596838,0.0747058689594269,0.0618916675448418,0.0474738143384457,0.0496549904346466,0.0399090498685837,43 -0.463072091341019,0.485370546579361,0.430398672819138,0.070897750556469,0.0753868967294693,0.061828400939703,0.0477456152439117,0.0515857115387917,0.039817675948143,44 -0.504301607608795,0.495000720024109,0.421750038862228,0.071284644305706,0.0756229311227798,0.0619377642869949,0.0486843213438988,0.0521590225398541,0.0400484018027782,45 -0.494013756513596,0.502004504203796,0.431962072849274,0.0704603344202042,0.075153686106205,0.0622560419142246,0.04722835496068,0.0514012612402439,0.039880596101284,46 -0.480952113866806,0.475219011306763,0.427944034337997,0.0712133646011353,0.0757130458950996,0.0625443011522293,0.0476561672985554,0.0518420785665512,0.0402946658432484,47 -0.4861159324646,0.516651332378387,0.448401153087616,0.0710133090615273,0.0765742212533951,0.0628931447863579,0.0490181632339954,0.0527153052389622,0.0413180962204933,48 -0.505605041980743,0.523734390735626,0.470708280801773,0.0711066871881485,0.0766011476516724,0.0636289045214653,0.0492411740124226,0.0540735982358456,0.0423084460198879,49 -0.513229548931122,0.550731658935547,0.487264752388,0.0714162662625313,0.0776983126997948,0.0654886662960052,0.0496645383536816,0.0559630356729031,0.0443988926708698,50 -0.523128151893616,0.541519641876221,0.508733510971069,0.0719589814543724,0.0785757303237915,0.0667508617043495,0.0495718084275723,0.0566712841391563,0.0457895845174789,51 -0.505810618400574,0.511520206928253,0.512334108352661,0.0727487280964851,0.078390434384346,0.0674923211336136,0.0494792275130749,0.0563753582537174,0.0470927283167839,52 -0.523990094661713,0.521157741546631,0.502269566059113,0.0725650414824486,0.0767486467957497,0.0678139925003052,0.0510885566473007,0.0540808290243149,0.047186266630888,53 -0.505142688751221,0.510516762733459,0.473371058702469,0.0712223723530769,0.0750300288200378,0.067938931286335,0.0487538911402225,0.0511478036642075,0.0453384555876255,54 -0.49169534444809,0.497860074043274,0.440884381532669,0.0709625855088234,0.0743403211236,0.0676973983645439,0.0484340563416481,0.0499401353299618,0.0442236810922623,55 -0.449224293231964,0.49097341299057,0.43197637796402,0.0712493658065796,0.0736074447631836,0.0669783055782318,0.0481879562139511,0.0487863756716251,0.0432323180139065,56 -0.462083280086517,0.458611458539963,0.420163929462433,0.0710224434733391,0.0725374296307564,0.0672811642289162,0.0468299798667431,0.0484821572899818,0.0426194556057453,57 -0.466422975063324,0.431519895792007,0.411749631166458,0.0716194584965706,0.0718199908733368,0.0670692101120949,0.0491987094283104,0.0477711223065853,0.0421844013035297,58 -0.470720857381821,0.452195525169373,0.402273863554001,0.0716342553496361,0.0719970166683197,0.0668240934610367,0.0485116802155972,0.0467447564005852,0.0414865128695965,59 -0.464846402406693,0.438703387975693,0.404926747083664,0.0720167830586433,0.0727354884147644,0.0666147843003273,0.0477447547018528,0.0472656972706318,0.0414320714771748,60 -0.466590017080307,0.44343689084053,0.405802458524704,0.0720655098557472,0.072361558675766,0.0671483352780342,0.0480720065534115,0.0469786375761032,0.0417016632854939,61 -0.461378246545792,0.444682002067566,0.396851778030396,0.0718182176351547,0.0714400708675385,0.0669265314936638,0.047651831060648,0.0463756546378136,0.0415653325617313,62 -0.461525678634644,0.406780183315277,0.386990875005722,0.0716791599988937,0.0715874135494232,0.0669709593057632,0.0474572516977787,0.0466903038322926,0.0415439382195473,63 -0.447014361619949,0.414845287799835,0.384565651416779,0.0707539543509483,0.071328230202198,0.0670299381017685,0.0461493171751499,0.045212522149086,0.0411532782018185,64 -0.425164729356766,0.408224016427994,0.378880858421326,0.0712318941950798,0.0714204683899879,0.0676426440477371,0.0461742058396339,0.0446786060929298,0.0415577478706837,65 -0.406535506248474,0.39065757393837,0.390029609203339,0.0721957013010979,0.0714574679732323,0.0679289177060127,0.0468821786344051,0.0455186292529106,0.0415354371070862,66 -0.429334431886673,0.421460598707199,0.386443197727203,0.0720773190259933,0.0723162442445755,0.068557009100914,0.0471111163496971,0.0465410239994526,0.0424121767282486,67 -0.408385515213013,0.423402607440948,0.384852766990662,0.0731761381030083,0.0739946663379669,0.0693683549761772,0.0479194596409798,0.0479996241629124,0.043307401239872,68 -0.450928092002869,0.433510303497314,0.387267470359802,0.0732240453362465,0.0757285431027412,0.070844329893589,0.0488691031932831,0.0505488254129887,0.0446740724146366,69 -0.42838642001152,0.458660960197449,0.397508889436722,0.0737349465489388,0.076910100877285,0.0725433081388473,0.0485723651945591,0.0513449050486088,0.0462205372750759,70 -0.457528233528137,0.455272883176804,0.401177316904068,0.0746021792292595,0.0782865285873413,0.0737910494208336,0.0504703745245934,0.0534177497029305,0.0476498752832413,71 -0.477989226579666,0.449814409017563,0.415226876735687,0.0757969841361046,0.0819070339202881,0.0762551799416542,0.0521196164190769,0.0565894842147827,0.0500268675386906,72 -0.471660286188126,0.483049720525742,0.41699144244194,0.0772641450166702,0.0833683088421822,0.0779938697814941,0.0538835860788822,0.0582899935543537,0.0516200289130211,73 -0.462039291858673,0.484837472438812,0.417837411165237,0.0793179050087929,0.0855541229248047,0.0807979106903076,0.0558264814317226,0.0610172972083092,0.0537474788725376,74 -0.494239538908005,0.474657744169235,0.410433322191238,0.080167755484581,0.0883776918053627,0.0834952890872955,0.0563570857048035,0.0627634450793266,0.0555526539683342,75 -0.481115281581879,0.4631627202034,0.405094832181931,0.0810613483190537,0.0898661091923714,0.085809163749218,0.0574533082544804,0.063525564968586,0.0566239841282368,76 -0.481957763433456,0.444837510585785,0.392141699790955,0.0815427601337433,0.0908975899219513,0.0876085460186005,0.0575459823012352,0.0625557452440262,0.0570836253464222,77 -0.46474814414978,0.416785925626755,0.384859055280685,0.0834897756576538,0.0924627110362053,0.0896750018000603,0.0571116954088211,0.0623503513634205,0.0581204779446125,78 -0.446739017963409,0.395158112049103,0.372885912656784,0.0857851952314377,0.0973858460783958,0.0923485606908798,0.0588888488709927,0.0653925687074661,0.0595557168126106,79 -0.425965458154678,0.364043891429901,0.341714292764664,0.0874398797750473,0.102611370384693,0.0957552641630173,0.0598357506096363,0.0677491873502731,0.0603233315050602,80 -0.401139229536056,0.364588558673859,0.329459756612778,0.0885151326656342,0.102042146027088,0.097852848470211,0.0583052486181259,0.0661919564008713,0.0615657567977905,81 -0.386102288961411,0.331744521856308,0.310481607913971,0.088083989918232,0.102688789367676,0.0995604023337364,0.056625984609127,0.064409427344799,0.0617730244994164,82 -0.352426558732986,0.287841856479645,0.287958323955536,0.0899283438920975,0.103480368852615,0.100912734866142,0.0579833500087261,0.0627572685480118,0.0609871000051498,83 -0.356676459312439,0.279404014348984,0.266594916582108,0.0904190465807915,0.104584082961082,0.10176607966423,0.0583710223436356,0.0623739063739777,0.0608399920165539,84 -0.353478521108627,0.253417283296585,0.25350296497345,0.0909158438444138,0.105099938809872,0.103342562913895,0.0577074661850929,0.0616596378386021,0.0614556968212128,85 -0.299452573060989,0.232542738318443,0.223194167017937,0.0930669605731964,0.105844244360924,0.104641474783421,0.0584431923925877,0.0609173066914082,0.0607141107320786,86 -0.257176458835602,0.220698863267899,0.218866571784019,0.0945220738649368,0.107813261449337,0.104784607887268,0.0576106905937195,0.0613766200840473,0.0604684762656689,87 -0.257824391126633,0.227517262101173,0.207409575581551,0.092997707426548,0.104204557836056,0.102655082941055,0.0560135245323181,0.0604200474917889,0.058957364410162,88 -0.238055855035782,0.211737111210823,0.201141089200974,0.0910771638154984,0.0975786820054054,0.0979374051094055,0.0540145039558411,0.0561005882918835,0.0565716363489628,89 -0.238567247986794,0.227371960878372,0.204761475324631,0.0879477933049202,0.0902053341269493,0.0918598845601082,0.0529557727277279,0.0529294200241566,0.0533599480986595,90 -0.216722473502159,0.234640255570412,0.217169404029846,0.0868481546640396,0.0833379998803139,0.0849183723330498,0.0509745627641678,0.0489254035055637,0.0499787591397762,91 -0.231909289956093,0.24814073741436,0.230379953980446,0.0836911797523499,0.0766035988926888,0.0774712786078453,0.0496042557060719,0.0455812141299248,0.0460172258317471,92 -0.223331198096275,0.256241410970688,0.256796300411224,0.0792895033955574,0.070108599960804,0.0709625855088234,0.0472096875309944,0.0411984920501709,0.0422120206058025,93 -0.246469661593437,0.287815362215042,0.283796429634094,0.0738609507679939,0.0632559433579445,0.0643703117966652,0.043757751584053,0.0369679853320122,0.0378340892493725,94 -0.270621925592422,0.283220708370209,0.280689835548401,0.068959467113018,0.0578584857285023,0.0592832416296005,0.0411073453724384,0.032867755740881,0.0339051820337772,95 \ No newline at end of file diff --git a/src/test/resources/edu/ie3/simona/model/participant/load/standard_load_profiles_test.csv b/src/test/resources/edu/ie3/simona/model/participant/load/standard_load_profiles_test.csv deleted file mode 100644 index 64780bf40f..0000000000 --- a/src/test/resources/edu/ie3/simona/model/participant/load/standard_load_profiles_test.csv +++ /dev/null @@ -1,97 +0,0 @@ -g0SuSa,g0SuSu,g0SuWd,g0TrSa,g0TrSu,g0TrWd,g0WiSa,g0WiSu,g0WiWd,h0SuSa,h0SuSu,h0SuWd,h0TrSa,h0TrSu,h0TrWd,h0WiSa,h0WiSu,h0WiWd,l0SuSa,l0SuSu,l0SuWd,l0TrSa,l0TrSu,l0TrWd,l0WiSa,l0WiSu,l0WiWd,quarterHour -74.6,68.8,71.5,75.8,68.3,73.0,70.0,63.2,65.5,89.8,100.1,86.3,80.2,93.4,77.8,70.8,87.5,67.6,67.2,62.6,66.1,71.4,64.8,70.3,73.9,68.3,72.4,0 -76.2,67.4,69.0,76.7,66.5,70.1,73.0,61.0,62.6,84.9,92.5,76.9,75.1,86.8,69.6,68.2,81.1,60.8,65.6,60.6,63.1,69.4,62.7,67.1,73.0,66.0,69.4,1 -77.7,65.7,66.3,77.7,64.6,67.1,75.9,58.9,59.6,80.7,85.9,68.8,70.7,81.2,62.4,65.9,75.0,54.9,64.3,58.9,60.5,68.0,61.3,64.3,72.6,64.3,66.9,2 -78.5,63.5,63.5,78.5,62.6,64.5,77.6,57.0,57.0,76.6,79.9,62.4,66.6,75.7,56.6,63.3,69.1,49.9,63.0,57.4,58.4,66.7,60.2,62.1,72.3,63.0,64.8,3 -77.9,60.9,60.9,78.5,60.3,62.3,77.1,55.3,54.8,71.7,74.1,58.0,62.3,70.1,52.5,59.5,63.4,46.2,61.4,56.2,56.7,65.3,59.2,60.3,71.5,62.1,63.2,4 -76.3,58.0,58.6,77.8,57.9,60.6,75.0,53.7,53.1,66.6,68.7,55.3,58.0,64.5,49.7,55.0,58.2,43.6,59.7,55.2,55.4,63.9,58.2,59.1,70.4,61.4,61.9,5 -74.1,55.4,56.6,76.6,55.5,59.2,72.1,52.1,51.7,61.6,63.9,53.6,54.1,59.3,47.9,50.5,53.6,41.9,58.0,54.4,54.4,62.4,57.3,58.1,69.1,60.8,61.0,6 -71.9,53.3,55.1,74.7,53.3,57.9,69.1,50.5,50.5,57.4,59.9,52.4,50.8,54.9,46.6,46.6,49.9,40.8,56.5,53.7,53.7,61.1,56.5,57.4,67.6,60.2,60.2,7 -70.2,52.0,54.2,72.6,51.2,56.7,66.8,48.7,49.4,54.5,57.0,51.3,48.4,51.7,45.5,43.9,47.3,40.1,55.2,53.2,53.2,60.0,55.8,56.9,66.2,59.5,59.6,8 -68.9,51.4,53.7,70.4,49.5,55.6,65.1,46.9,48.5,52.6,55.0,50.3,46.8,49.4,44.5,42.3,45.5,39.6,54.2,52.7,52.7,59.1,55.3,56.4,64.8,58.8,59.2,9 -67.9,51.0,53.4,68.5,48.0,54.7,64.1,45.2,47.9,51.4,53.5,49.2,45.7,47.8,43.8,41.4,44.2,39.4,53.4,52.3,52.3,58.3,54.9,56.0,63.4,58.0,58.8,10 -67.3,50.5,53.3,67.3,46.7,54.2,63.5,43.9,47.7,50.8,52.4,48.3,44.9,46.6,43.3,40.8,43.3,39.1,52.8,51.9,51.9,57.4,54.7,55.6,62.1,57.4,58.4,11 -66.9,49.6,53.1,67.0,45.7,54.1,63.4,43.0,47.9,50.3,51.5,47.5,44.4,45.5,43.0,40.3,42.4,38.8,52.3,51.4,51.4,56.6,54.6,55.1,60.7,57.0,57.9,12 -66.7,48.5,53.2,67.5,44.9,54.6,63.6,42.5,48.7,50.0,50.8,46.9,43.9,44.5,43.0,39.9,41.5,38.6,51.8,50.9,51.0,55.7,54.6,54.5,59.4,56.7,57.4,13 -66.8,47.5,54.1,68.3,44.3,55.8,64.0,42.2,50.2,49.9,50.2,46.5,43.5,43.8,43.1,39.5,40.7,38.3,51.4,50.5,50.5,55.1,54.7,54.1,58.3,56.6,56.9,14 -67.3,46.7,56.1,69.1,43.9,57.9,64.5,42.0,52.3,49.9,49.9,46.6,43.3,43.3,43.3,39.1,40.0,38.3,50.9,50.0,50.0,54.7,54.7,53.7,57.4,56.5,56.5,15 -68.0,46.4,59.4,69.7,43.6,60.9,65.0,41.9,55.1,50.1,49.9,47.1,43.1,43.1,43.4,38.8,39.3,38.4,50.5,49.6,49.6,54.6,54.5,53.6,56.9,56.4,56.3,16 -69.0,46.5,63.3,70.1,43.5,64.4,65.6,41.8,58.2,50.4,50.0,48.0,43.1,43.1,43.7,38.5,38.8,38.8,50.2,49.4,49.4,54.7,54.3,53.7,56.8,56.5,56.3,17 -70.0,46.6,67.2,70.5,43.6,68.0,66.3,41.8,61.2,50.7,50.1,49.3,43.1,43.2,44.2,38.3,38.5,39.3,50.0,49.5,49.5,55.1,54.3,54.0,57.0,56.8,56.6,18 -71.0,46.7,70.1,71.0,43.9,71.0,67.3,42.0,63.5,50.8,49.9,50.8,43.3,43.3,44.9,38.3,38.3,40.0,50.0,50.0,50.0,55.6,54.7,54.7,57.4,57.4,57.4,19 -71.8,46.6,71.4,71.8,44.5,73.2,68.5,42.6,65.0,50.8,49.5,52.7,43.6,43.3,46.3,38.5,38.3,40.9,50.3,51.2,51.2,56.1,55.5,55.6,58.2,58.5,58.8,20 -72.4,46.3,71.6,72.5,45.1,74.7,69.9,43.4,66.0,50.9,48.9,55.6,44.2,43.3,48.9,39.1,38.4,43.1,50.9,53.1,52.9,56.6,57.3,57.2,59.2,60.5,60.9,21 -72.8,46.0,71.2,73.0,45.6,75.7,71.4,44.4,67.1,51.6,48.4,60.5,45.4,43.2,53.7,40.3,38.7,47.7,51.7,56.0,55.3,57.4,60.3,59.8,60.5,63.7,63.8,22 -72.9,45.8,71.0,72.9,45.8,76.6,72.9,45.8,69.1,53.3,48.3,68.2,47.4,43.3,61.6,42.4,39.1,55.8,52.8,60.2,58.4,58.4,64.8,63.9,62.1,68.5,67.6,23 -73.0,45.9,71.5,72.2,45.6,77.7,74.4,47.4,72.5,56.2,48.7,79.2,50.5,43.5,72.9,45.6,39.7,68.0,54.4,65.8,62.2,59.9,71.3,69.8,64.1,75.3,72.5,24 -73.9,46.3,73.1,72.2,45.3,79.3,76.8,49.0,77.1,60.4,49.8,92.0,54.9,44.3,86.3,49.9,40.4,82.8,57.2,73.2,67.4,62.9,79.8,77.6,67.4,84.1,79.0,25 -77.0,47.2,75.9,74.4,45.2,81.9,81.0,50.4,82.9,65.8,51.9,104.7,60.7,46.0,100.1,55.3,41.3,98.0,62.2,82.6,74.8,68.8,90.6,87.6,72.9,94.8,87.7,26 -83.2,48.6,80.4,80.4,45.8,86.0,87.8,51.4,89.7,72.4,54.9,115.7,68.2,49.1,112.4,61.6,42.4,111.5,70.4,94.5,85.2,78.7,103.7,100.0,81.5,107.4,99.1,27 -93.3,50.5,86.6,91.0,47.2,91.9,98.0,51.8,97.6,80.0,59.2,123.5,77.5,53.9,121.8,68.9,44.0,121.6,82.4,108.8,98.9,93.4,119.3,114.8,93.9,121.9,113.6,28 -106.2,52.6,95.1,105.2,49.1,100.4,110.7,51.7,107.3,88.5,64.9,128.6,87.9,60.4,128.5,77.1,46.6,128.5,97.4,124.7,114.9,111.6,136.3,131.3,109.4,137.8,130.5,29 -120.5,54.3,106.3,121.2,51.0,111.9,124.8,51.2,119.9,97.4,72.3,132.0,98.6,68.8,132.9,86.1,51.1,132.7,114.2,141.0,131.8,131.4,153.8,148.6,126.9,155.0,148.9,30 -134.5,55.1,120.5,137.4,52.3,127.1,139.2,50.5,136.4,106.5,81.6,134.8,109.0,79.1,135.7,95.7,58.3,134.8,131.5,156.5,148.2,151.0,170.4,165.8,145.4,173.2,167.7,31 -147.2,54.8,137.8,152.1,52.8,145.9,153.0,49.6,157.1,115.6,92.9,137.8,118.4,91.1,137.2,105.8,68.6,135.4,148.2,170.1,162.6,168.6,185.3,181.9,163.8,191.7,185.8,32 -157.8,53.9,156.3,164.9,52.9,166.2,165.4,49.0,179.5,124.4,105.6,140.7,126.7,104.3,137.7,115.8,81.3,134.8,162.8,180.5,173.9,183.2,197.0,195.1,181.0,208.6,201.7,33 -166.2,53.2,173.4,175.1,53.1,185.2,175.9,48.8,200.5,132.8,119.0,143.2,133.8,118.0,137.7,124.9,95.2,133.1,173.8,186.6,180.6,193.7,204.6,203.4,195.6,221.4,213.6,34 -171.9,53.3,186.9,182.2,54.2,200.0,184.1,49.5,216.8,140.7,132.3,144.8,139.8,131.5,137.3,132.3,109.0,130.7,179.7,187.1,181.6,199.2,206.6,204.7,206.6,227.9,219.5,35 -175.0,54.7,195.0,186.1,56.4,208.5,189.5,51.3,226.2,147.8,144.8,145.3,144.7,144.2,136.9,137.6,121.9,127.7,179.6,181.6,176.1,198.9,202.4,198.0,212.9,226.4,218.5,36 -176.1,57.2,198.7,187.5,59.5,212.1,192.8,53.8,230.0,154.0,156.2,144.9,148.8,155.5,136.4,141.1,133.7,124.6,174.9,171.8,166.1,194.1,193.4,185.5,214.6,218.6,211.6,37 -176.3,60.0,199.8,187.7,62.7,212.6,195.1,56.8,230.4,158.9,166.0,143.8,152.4,165.3,135.7,143.3,144.4,121.5,167.2,160.1,154.1,186.2,181.5,170.6,212.1,206.6,200.8,38 -176.6,62.6,200.0,187.8,65.4,212.1,197.1,59.8,229.9,162.3,174.0,142.3,155.6,173.1,134.8,144.8,154.0,119.0,158.4,149.1,142.6,176.9,168.6,156.5,205.6,192.7,188.0,39 -177.7,64.5,200.6,188.7,67.2,212.2,199.8,62.6,230.0,164.1,180.0,140.8,158.9,178.8,133.7,146.0,162.6,117.3,150.1,140.8,133.8,167.6,156.5,145.9,195.9,178.9,175.0,40 -179.4,65.9,201.9,190.1,68.5,213.1,202.7,65.1,231.2,164.7,184.6,139.5,162.0,183.1,132.4,147.2,170.5,116.2,143.4,135.6,128.2,159.6,146.4,139.0,185.0,167.0,163.4,41 -181.3,67.0,203.6,191.9,69.6,214.6,205.4,67.2,233.0,165.0,188.7,138.5,164.9,187.0,131.4,148.4,178.0,115.7,139.1,133.7,125.8,154.0,139.6,135.5,175.4,158.1,154.5,42 -183.1,68.2,205.6,193.4,71.0,216.8,207.4,69.1,235.5,165.6,193.1,138.2,167.3,191.4,130.7,149.8,185.6,115.7,138.0,135.2,126.9,151.9,137.1,135.2,169.5,153.8,150.1,43 -184.4,69.7,207.6,194.5,73.1,219.2,208.4,70.8,238.1,167.1,198.3,138.6,169.2,197.0,130.6,151.5,193.3,116.1,140.4,139.9,131.2,153.9,139.4,137.5,168.9,154.6,150.7,44 -185.2,71.4,209.1,195.1,75.6,221.2,208.3,72.2,240.0,169.4,203.7,140.1,170.8,203.0,131.5,153.5,200.6,117.0,144.4,145.6,136.6,157.9,144.3,140.7,171.3,158.2,154.1,45 -185.3,73.2,209.6,195.3,78.2,222.0,207.4,73.5,240.4,172.4,208.7,142.6,172.5,208.5,133.6,156.0,206.6,118.7,147.5,149.8,140.9,161.3,148.8,143.1,173.5,161.5,157.4,46 -185.0,74.7,208.4,195.3,80.4,220.5,205.6,74.7,238.3,175.6,212.2,146.5,174.8,212.2,137.3,159.0,210.6,121.5,147.3,150.1,141.7,161.2,150.1,142.6,172.3,161.2,157.5,47 -184.1,76.0,205.1,194.9,81.9,216.3,203.0,76.0,233.0,179.0,213.7,151.5,177.7,213.5,142.6,162.4,211.8,125.4,142.0,144.6,137.4,155.8,146.0,138.2,165.4,155.2,152.2,48 -182.4,77.0,200.0,193.9,82.8,209.8,199.7,77.3,225.1,181.9,212.8,156.7,180.8,211.8,148.2,165.8,210.2,129.6,133.1,135.3,129.4,146.7,138.1,130.9,154.5,145.4,143.2,49 -179.7,77.8,193.8,191.7,83.2,201.9,195.6,78.4,215.7,183.7,209.0,160.7,183.1,207.0,152.8,168.4,205.9,133.0,122.7,124.5,119.9,135.9,128.5,122.5,142.0,134.4,132.7,50 -175.7,78.5,186.9,187.8,83.2,193.4,190.6,79.4,205.6,183.9,202.3,162.3,183.9,198.9,154.8,169.8,198.9,134.8,113.0,114.9,111.2,126.0,119.5,114.9,130.6,125.0,123.2,51 -170.1,79.1,179.9,181.9,82.8,185.0,184.6,80.1,195.7,182.1,192.4,160.5,182.7,187.6,153.2,169.4,189.6,134.2,105.7,108.4,104.8,118.5,112.9,109.4,122.1,119.4,116.5,52 -163.1,79.5,173.4,174.1,82.1,177.4,177.5,80.5,186.7,178.7,180.9,156.1,179.8,174.6,148.9,167.6,178.7,131.7,100.6,104.7,100.8,113.1,108.6,105.9,116.1,116.9,112.5,53 -154.6,79.3,168.1,164.8,80.9,170.9,168.9,80.3,179.2,174.1,169.2,150.2,175.8,161.7,143.2,164.8,167.3,128.0,96.8,102.8,98.5,108.8,105.7,103.9,111.6,116.1,110.3,54 -144.8,78.5,164.4,154.2,79.4,166.3,158.8,79.4,173.8,169.0,159.0,144.0,171.5,150.6,137.3,161.5,156.5,124.0,93.6,101.9,97.3,104.7,103.7,102.8,107.4,115.8,109.3,55 -133.9,76.8,163.0,142.6,77.5,163.9,147.2,77.8,171.0,163.7,151.4,138.4,167.2,142.8,132.4,158.1,147.0,120.2,90.3,101.1,96.5,100.0,102.0,102.1,102.9,114.8,108.8,56 -122.7,74.4,163.5,130.9,75.3,163.6,134.8,75.7,170.7,158.9,146.0,133.6,163.1,137.6,128.4,154.9,138.9,116.8,87.1,100.4,96.2,95.3,100.5,101.7,98.2,113.2,108.5,57 -112.0,71.8,165.4,119.8,73.1,165.1,122.7,73.4,172.7,154.6,141.6,129.4,159.2,133.9,124.8,151.8,132.1,113.7,84.4,99.7,96.2,91.1,99.2,101.4,94.0,111.3,108.4,58 -102.8,69.1,168.2,110.3,71.0,168.2,112.1,71.0,176.6,151.5,137.3,125.7,155.6,130.7,121.5,149.0,126.5,110.7,82.4,99.1,96.3,88.0,98.2,101.0,90.8,109.3,108.4,59 -95.7,66.8,171.5,102.7,69.2,172.5,103.8,68.9,182.1,149.6,132.4,122.4,152.3,127.1,118.1,146.5,122.0,107.9,81.5,98.6,96.7,86.6,97.4,100.4,88.9,107.5,108.3,60 -90.4,64.8,174.6,96.9,67.6,177.3,97.4,67.0,188.2,148.6,127.0,119.6,149.5,123.1,114.8,144.4,118.1,105.5,81.3,98.1,97.0,86.3,96.7,99.9,88.2,106.1,108.3,61 -86.4,63.1,177.2,92.1,66.1,181.4,92.6,65.2,193.9,148.0,121.6,117.4,147.2,119.0,111.7,142.7,114.8,103.5,81.4,97.7,97.2,86.7,96.1,99.4,88.0,105.4,108.6,62 -83.2,61.7,178.5,87.8,64.5,184.1,88.8,63.5,198.1,147.3,116.5,115.7,145.7,114.9,109.0,141.5,111.5,102.4,81.5,97.3,97.3,87.1,95.4,99.1,88.0,105.6,109.3,63 -80.3,60.4,178.2,83.6,62.8,184.5,85.6,62.0,200.1,146.2,112.2,114.6,145.0,111.0,106.9,140.9,108.3,102.2,81.3,96.9,97.0,87.1,94.7,99.2,87.9,106.9,110.6,64 -77.8,59.7,176.8,79.7,61.5,183.6,83.3,61.2,200.7,145.0,108.9,114.2,145.4,107.7,105.7,141.7,105.9,103.2,81.3,96.9,96.8,87.3,94.5,99.9,88.5,109.8,113.1,65 -75.9,59.7,175.1,76.9,61.2,182.1,82.4,61.8,200.9,144.1,106.7,114.6,146.7,105.6,105.5,144.9,105.2,105.6,81.8,97.5,97.1,88.5,95.8,101.6,90.9,114.4,117.4,66 -74.7,60.7,173.8,75.7,62.6,181.3,83.2,64.5,201.8,144.0,105.7,115.7,149.0,104.9,106.5,151.5,107.4,109.9,83.4,99.1,98.2,91.7,99.1,104.7,96.3,121.3,124.1,67 -74.4,63.0,173.2,76.5,65.9,181.5,85.8,69.5,204.0,144.9,106.1,117.6,152.3,106.0,109.1,161.9,112.9,116.0,86.6,102.1,100.6,97.4,105.2,109.4,105.5,130.7,133.7,68 -74.8,66.0,172.4,78.6,70.4,181.7,89.5,76.0,205.8,146.9,107.7,120.3,156.4,108.8,113.1,174.6,120.9,123.7,91.7,106.8,104.8,105.7,114.0,116.2,117.8,142.4,146.0,69 -75.6,69.2,170.2,81.1,75.0,180.0,93.4,82.6,205.5,150.0,110.4,123.9,161.5,113.2,118.3,187.4,130.0,132.6,99.2,113.7,111.5,116.2,125.3,125.6,132.7,156.1,160.5,70 -76.6,71.9,165.4,83.2,78.5,174.7,96.2,87.8,200.9,154.0,114.0,128.2,167.3,119.0,124.8,198.1,139.0,142.3,109.3,123.2,121.3,128.8,138.9,138.0,149.1,171.4,176.9,71 -77.6,73.9,157.1,84.1,80.1,164.8,97.4,90.8,190.7,158.8,118.3,133.2,173.7,126.0,132.4,205.2,146.8,152.4,122.1,135.4,134.4,142.9,154.5,153.5,166.4,187.8,194.5,72 -78.5,75.1,146.1,84.3,80.4,151.5,97.4,92.1,176.6,164.0,123.4,138.9,180.4,133.7,140.6,209.1,153.5,162.2,135.8,148.9,148.9,157.3,170.3,170.2,183.4,204.1,211.5,73 -79.1,76.0,133.8,84.1,80.2,137.0,96.8,92.3,160.5,169.2,129.2,145.1,186.7,141.5,149.1,211.1,159.7,171.2,148.6,161.8,162.6,170.7,184.6,185.8,199.0,218.5,226.1,74 -79.4,76.6,121.5,84.1,80.4,123.3,96.2,92.5,144.8,174.0,135.7,151.5,192.3,149.0,157.3,212.2,165.6,178.9,158.4,172.3,173.2,181.6,195.4,198.2,212.1,229.7,236.2,75 -79.4,77.3,110.1,84.7,81.4,112.1,96.3,93.1,131.3,177.8,142.8,157.9,196.5,155.5,164.9,213.2,171.5,184.7,163.6,178.8,178.9,188.7,201.4,205.5,221.5,236.2,240.4,76 -79.4,78.2,100.5,85.8,83.1,103.4,96.8,94.0,120.1,180.5,149.8,163.8,199.0,160.6,171.1,213.0,176.4,188.2,164.4,181.1,179.8,191.7,202.7,207.5,226.2,237.4,238.7,77 -79.6,79.2,93.2,87.3,85.1,97.3,97.2,94.6,111.3,181.8,155.7,168.3,199.4,163.6,175.2,210.4,179.1,188.9,161.5,179.2,176.5,190.6,199.9,204.7,224.9,232.9,231.5,78 -80.4,80.4,88.8,88.8,86.9,93.4,97.2,94.4,104.6,181.4,159.8,170.6,197.3,164.0,176.5,203.9,178.1,186.4,155.6,173.2,169.5,185.3,193.6,197.3,216.8,222.3,219.5,79 -81.9,81.6,87.4,90.1,88.1,91.6,96.3,93.0,100.0,179.3,161.4,170.4,192.4,161.5,174.5,192.9,172.9,180.7,147.4,163.4,159.6,175.8,184.4,185.9,201.3,205.7,203.4,80 -83.9,82.9,88.1,91.2,88.7,91.1,94.8,90.8,96.7,175.7,160.8,168.3,185.2,157.1,170.5,179.0,164.7,172.7,137.8,151.4,148.1,163.7,173.4,172.1,181.3,185.5,184.9,81 -85.7,83.8,89.6,91.7,88.6,91.1,92.8,88.0,94.1,171.0,159.0,165.3,176.6,152.2,165.7,164.4,155.6,163.9,127.9,138.9,136.4,150.5,161.6,157.7,160.3,164.7,166.1,82 -86.9,84.1,90.6,91.6,87.8,90.6,90.6,85.0,91.6,165.6,156.5,162.3,167.3,148.2,161.5,151.5,147.3,155.6,118.6,127.8,126.0,138.0,150.1,144.5,141.7,146.4,149.1,83 -87.0,83.7,90.2,90.6,86.4,89.2,88.5,82.1,88.5,160.1,153.9,160.1,157.9,145.9,158.9,141.9,141.4,148.9,110.7,119.5,117.9,127.4,139.6,133.8,128.3,132.6,135.6,84 -86.3,82.7,88.5,89.0,84.4,87.2,86.4,79.4,85.2,155.1,151.5,158.4,149.5,144.7,157.2,135.3,137.2,143.4,104.1,113.5,111.9,118.8,130.3,125.5,119.3,123.0,125.2,85 -84.9,81.2,86.3,87.1,82.0,85.0,84.3,76.9,82.1,151.1,149.3,156.8,142.9,143.6,155.4,131.0,133.7,138.4,98.5,108.8,107.1,111.6,122.1,118.8,113.2,116.3,117.3,86 -83.2,79.4,84.1,85.0,79.4,83.2,82.2,74.7,79.4,149.0,147.3,154.8,139.0,141.5,152.3,128.2,129.8,133.2,93.6,104.7,102.8,105.6,114.9,113.0,108.4,111.2,111.2,87 -81.3,77.5,82.4,83.1,76.7,82.1,80.1,72.8,77.6,148.9,145.4,151.9,138.3,137.5,147.2,126.1,124.8,127.2,88.9,100.2,98.5,100.3,108.4,107.7,103.7,106.6,106.1,88 -79.3,75.5,81.2,81.2,74.1,81.7,78.0,71.1,76.4,149.6,143.0,147.9,139.2,131.8,140.3,124.1,118.6,120.5,84.6,95.4,94.1,95.5,102.5,102.5,98.8,102.1,101.7,89 -77.4,73.3,80.3,79.4,71.5,81.6,75.9,69.3,75.6,149.4,139.2,142.5,139.5,124.7,132.1,121.6,111.6,113.3,80.6,90.4,89.6,90.9,97.0,97.6,93.9,97.6,97.7,90 -75.7,71.0,79.4,77.6,69.1,81.3,73.8,67.3,74.7,146.5,133.2,135.7,137.3,116.5,123.2,118.2,104.0,105.7,76.9,85.2,85.2,86.1,91.7,92.6,88.9,92.6,93.6,91 -74.1,68.6,78.4,75.7,67.0,80.6,71.7,65.0,73.7,139.8,124.4,127.2,131.1,107.6,114.0,113.4,96.2,98.0,73.4,80.0,81.0,81.1,86.4,87.7,84.0,87.2,89.1,92 -72.7,66.4,77.2,73.8,65.3,79.5,69.7,62.6,72.3,130.3,113.8,117.5,121.9,98.4,104.8,107.4,88.4,90.2,70.3,75.0,76.9,76.1,81.2,82.9,79.3,81.6,84.6,93 -71.3,64.6,75.7,71.9,64.1,77.9,67.6,60.4,70.5,119.5,102.5,107.1,111.5,89.2,95.6,100.8,80.7,82.5,67.4,70.5,73.1,71.5,76.4,78.3,75.0,76.5,80.1,94 -70.1,63.5,73.8,70.1,63.5,75.7,65.4,58.9,68.2,109.0,91.6,96.5,101.5,80.7,86.6,94.1,73.2,74.9,64.8,66.7,69.5,67.6,72.3,74.1,71.3,72.3,76.0,95 diff --git a/src/test/scala/edu/ie3/simona/agent/grid/DBFSAlgorithmCenGridSpec.scala b/src/test/scala/edu/ie3/simona/agent/grid/DBFSAlgorithmCenGridSpec.scala index 8d23af9c46..6ad9c63c93 100644 --- a/src/test/scala/edu/ie3/simona/agent/grid/DBFSAlgorithmCenGridSpec.scala +++ b/src/test/scala/edu/ie3/simona/agent/grid/DBFSAlgorithmCenGridSpec.scala @@ -56,6 +56,7 @@ class DBFSAlgorithmCenGridSpec private val runtimeEvents: TestProbe[RuntimeEvent] = TestProbe("runtimeEvents") private val primaryService = TestProbe("primaryService") + private val loadProfileService = TestProbe("loadProfileService") private val weatherService = TestProbe("weatherService") private val superiorGridAgent = SuperiorGA( @@ -78,6 +79,7 @@ class DBFSAlgorithmCenGridSpec scheduler = scheduler.ref, runtimeEventListener = runtimeEvents.ref, primaryServiceProxy = primaryService.ref.toClassic, + loadProfiles = loadProfileService.ref.toClassic, weather = weatherService.ref.toClassic, evDataService = None, ) diff --git a/src/test/scala/edu/ie3/simona/agent/grid/DBFSAlgorithmFailedPowerFlowSpec.scala b/src/test/scala/edu/ie3/simona/agent/grid/DBFSAlgorithmFailedPowerFlowSpec.scala index a1047edeff..f4bc8af664 100644 --- a/src/test/scala/edu/ie3/simona/agent/grid/DBFSAlgorithmFailedPowerFlowSpec.scala +++ b/src/test/scala/edu/ie3/simona/agent/grid/DBFSAlgorithmFailedPowerFlowSpec.scala @@ -49,6 +49,7 @@ class DBFSAlgorithmFailedPowerFlowSpec private val runtimeEvents: TestProbe[RuntimeEvent] = TestProbe("runtimeEvents") private val primaryService = TestProbe("primaryService") + private val loadProfileService = TestProbe("loadProfileService") private val weatherService = TestProbe("weatherService") private val superiorGridAgent = SuperiorGA( @@ -63,6 +64,7 @@ class DBFSAlgorithmFailedPowerFlowSpec scheduler = scheduler.ref, runtimeEventListener = runtimeEvents.ref, primaryServiceProxy = primaryService.ref.toClassic, + loadProfiles = loadProfileService.ref.toClassic, weather = weatherService.ref.toClassic, evDataService = None, ) diff --git a/src/test/scala/edu/ie3/simona/agent/grid/DBFSAlgorithmParticipantSpec.scala b/src/test/scala/edu/ie3/simona/agent/grid/DBFSAlgorithmParticipantSpec.scala index fa43ee2d1e..b6a2cd461a 100644 --- a/src/test/scala/edu/ie3/simona/agent/grid/DBFSAlgorithmParticipantSpec.scala +++ b/src/test/scala/edu/ie3/simona/agent/grid/DBFSAlgorithmParticipantSpec.scala @@ -7,6 +7,7 @@ package edu.ie3.simona.agent.grid import edu.ie3.datamodel.graph.SubGridGate +import edu.ie3.datamodel.models.profile.LoadProfile import edu.ie3.simona.agent.EnvironmentRefs import edu.ie3.simona.agent.grid.GridAgentData.GridAgentInitData import edu.ie3.simona.agent.grid.GridAgentMessages.Responses.{ @@ -20,9 +21,13 @@ import edu.ie3.simona.ontology.messages.SchedulerMessage.{ Completion, ScheduleActivation, } +import edu.ie3.simona.ontology.messages.services.LoadProfileMessage.RegisterForLoadProfileService import edu.ie3.simona.ontology.messages.services.ServiceMessage import edu.ie3.simona.ontology.messages.services.ServiceMessage.PrimaryServiceRegistrationMessage -import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResponseMessage.RegistrationFailedMessage +import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResponseMessage.{ + RegistrationFailedMessage, + RegistrationSuccessfulMessage, +} import edu.ie3.simona.ontology.messages.{Activation, SchedulerMessage} import edu.ie3.simona.scheduler.ScheduleLock import edu.ie3.simona.test.common.model.grid.DbfsTestGridWithParticipants @@ -52,12 +57,16 @@ class DBFSAlgorithmParticipantSpec TestProbe("runtimeEvents") private val primaryService: TestProbe[ServiceMessage] = TestProbe("primaryService") + private val loadProfileService: TestProbe[ServiceMessage] = TestProbe( + "loadProfileService" + ) private val weatherService = TestProbe("weatherService") private val environmentRefs = EnvironmentRefs( scheduler = scheduler.ref, runtimeEventListener = runtimeEvents.ref, primaryServiceProxy = primaryService.ref.toClassic, + loadProfiles = loadProfileService.ref.toClassic, weather = weatherService.ref.toClassic, evDataService = None, ) diff --git a/src/test/scala/edu/ie3/simona/agent/grid/DBFSAlgorithmSupGridSpec.scala b/src/test/scala/edu/ie3/simona/agent/grid/DBFSAlgorithmSupGridSpec.scala index beb1408270..64eca358c1 100644 --- a/src/test/scala/edu/ie3/simona/agent/grid/DBFSAlgorithmSupGridSpec.scala +++ b/src/test/scala/edu/ie3/simona/agent/grid/DBFSAlgorithmSupGridSpec.scala @@ -55,6 +55,7 @@ class DBFSAlgorithmSupGridSpec TestProbe("runtimeEvents") private val primaryService: TestProbe[ServiceMessage] = TestProbe("primaryService") + private val loadProfileService = TestProbe("loadProfileService") private val weatherService = TestProbe("weatherService") private val hvGrid: TestProbe[GridAgent.Request] = TestProbe("hvGrid") @@ -62,6 +63,7 @@ class DBFSAlgorithmSupGridSpec scheduler = scheduler.ref, runtimeEventListener = runtimeEvents.ref, primaryServiceProxy = primaryService.ref.toClassic, + loadProfiles = loadProfileService.ref.toClassic, weather = weatherService.ref.toClassic, evDataService = None, ) diff --git a/src/test/scala/edu/ie3/simona/agent/participant/LoadAgentProfileModelCalculationSpec.scala b/src/test/scala/edu/ie3/simona/agent/participant/LoadAgentProfileModelCalculationSpec.scala index 6615308552..9f25e2ec08 100644 --- a/src/test/scala/edu/ie3/simona/agent/participant/LoadAgentProfileModelCalculationSpec.scala +++ b/src/test/scala/edu/ie3/simona/agent/participant/LoadAgentProfileModelCalculationSpec.scala @@ -9,6 +9,7 @@ package edu.ie3.simona.agent.participant import com.typesafe.config.ConfigFactory import edu.ie3.datamodel.models.input.system.LoadInput import edu.ie3.datamodel.models.input.system.characteristic.QV +import edu.ie3.datamodel.models.profile.BdewStandardLoadProfile import edu.ie3.simona.agent.ValueStore import edu.ie3.simona.agent.grid.GridAgentMessages.{ AssetPowerChangedMessage, @@ -16,14 +17,11 @@ import edu.ie3.simona.agent.grid.GridAgentMessages.{ } import edu.ie3.simona.agent.participant.ParticipantAgent.RequestAssetPowerMessage import edu.ie3.simona.agent.participant.data.Data.PrimaryData.ComplexPower +import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService.ActorLoadProfileService import edu.ie3.simona.agent.participant.load.LoadAgent.ProfileLoadAgent import edu.ie3.simona.agent.participant.statedata.BaseStateData.ParticipantModelBaseStateData -import edu.ie3.simona.agent.participant.statedata.ParticipantStateData.{ - ParticipantInitializeStateData, - ParticipantInitializingStateData, - ParticipantUninitializedStateData, - SimpleInputContainer, -} +import edu.ie3.simona.agent.participant.statedata.DataCollectionStateData +import edu.ie3.simona.agent.participant.statedata.ParticipantStateData._ import edu.ie3.simona.agent.state.AgentState.{Idle, Uninitialized} import edu.ie3.simona.agent.state.ParticipantAgentState.HandleInformation import edu.ie3.simona.config.SimonaConfig @@ -32,8 +30,16 @@ import edu.ie3.simona.event.notifier.NotifierConfig import edu.ie3.simona.model.participant.load.{LoadModelBehaviour, LoadReference} import edu.ie3.simona.ontology.messages.Activation import edu.ie3.simona.ontology.messages.SchedulerMessage.Completion +import edu.ie3.simona.ontology.messages.services.LoadProfileMessage.{ + LoadProfileData, + ProvideLoadProfileValue, + RegisterForLoadProfileService, +} import edu.ie3.simona.ontology.messages.services.ServiceMessage.PrimaryServiceRegistrationMessage -import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResponseMessage.RegistrationFailedMessage +import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResponseMessage.{ + RegistrationFailedMessage, + RegistrationSuccessfulMessage, +} import edu.ie3.simona.test.ParticipantAgentSpec import edu.ie3.simona.test.common.model.participant.LoadTestData import edu.ie3.simona.util.ConfigUtil @@ -41,7 +47,7 @@ import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK import edu.ie3.util.scala.quantities.{Megavars, ReactivePower, Vars} import org.apache.pekko.actor.ActorSystem import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps -import org.apache.pekko.testkit.TestFSMRef +import org.apache.pekko.testkit.{TestFSMRef, TestProbe} import org.apache.pekko.util.Timeout import org.scalatest.PrivateMethodTester import squants.Each @@ -89,7 +95,15 @@ class LoadAgentProfileModelCalculationSpec loadConfigUtil.getOrDefault[LoadRuntimeConfig]( voltageSensitiveInput.getUuid ) - private val services = Iterable.empty + + protected val loadProfileService: TestProbe = TestProbe( + "loadProfileServiceProbe" + ) + + private val noServices = Iterable.empty + private val withServices = Iterable( + ActorLoadProfileService(loadProfileService.ref) + ) private val resolution = simonaConfig.simona.powerflow.resolution.getSeconds private implicit val powerTolerance: squants.Power = Watts(0.1) @@ -103,7 +117,71 @@ class LoadAgentProfileModelCalculationSpec ]( inputModel = voltageSensitiveInput, modelConfig = modelConfig, - secondaryDataServices = services, + secondaryDataServices = noServices, + simulationStartDate = simulationStartDate, + simulationEndDate = simulationEndDate, + resolution = resolution, + requestVoltageDeviationThreshold = + simonaConfig.simona.runtime.participant.requestVoltageDeviationThreshold, + outputConfig = defaultOutputConfig, + primaryServiceProxy = primaryServiceProxy.ref, + ) + + "be instantiated correctly" in { + val loadAgent = TestFSMRef( + new ProfileLoadAgent( + scheduler = scheduler.ref, + initStateData = initStateData, + listener = systemListener, + ) + ) + + loadAgent.stateName shouldBe Uninitialized + // ParticipantUninitializedStateData is an empty class (due to typing). If it contains content one day + inside(loadAgent.stateData) { + case _: ParticipantUninitializedStateData[_] => succeed + case _ => + fail( + s"Expected $ParticipantUninitializedStateData, but got ${loadAgent.stateData}." + ) + } + } + + "fail initialisation and stop agent" in { + val deathProbe = TestProbe("deathProbe") + + val pvAgent = TestFSMRef( + new ProfileLoadAgent( + scheduler = scheduler.ref, + initStateData = initStateData, + listener = systemListener, + ) + ) + + scheduler.send(pvAgent, Activation(INIT_SIM_TICK)) + + deathProbe.watch(pvAgent.ref) + + /* Refuse registration with primary service */ + primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage] + primaryServiceProxy.send( + pvAgent, + RegistrationFailedMessage(primaryServiceProxy.ref), + ) + + deathProbe.expectTerminated(pvAgent.ref) + } + } + + "A load agent with profile model calculation depending on secondary data service" should { + val initStateData = ParticipantInitializeStateData[ + LoadInput, + LoadRuntimeConfig, + ComplexPower, + ]( + inputModel = voltageSensitiveInput, + modelConfig = modelConfig, + secondaryDataServices = withServices, simulationStartDate = simulationStartDate, simulationEndDate = simulationEndDate, resolution = resolution, @@ -163,7 +241,7 @@ class LoadAgentProfileModelCalculationSpec ) => inputModel shouldBe SimpleInputContainer(voltageSensitiveInput) modelConfig shouldBe modelConfig - secondaryDataServices shouldBe services + secondaryDataServices shouldBe withServices simulationStartDate shouldBe this.simulationStartDate simulationEndDate shouldBe this.simulationEndDate resolution shouldBe this.resolution @@ -180,43 +258,81 @@ class LoadAgentProfileModelCalculationSpec RegistrationFailedMessage(primaryServiceProxy.ref), ) - /* Expect a completion notification */ - scheduler.expectMsg(Completion(loadAgent.toTyped, Some(0))) + /* Expect a registration message */ + loadProfileService.expectMsg( + RegisterForLoadProfileService(BdewStandardLoadProfile.H0) + ) /* ... as well as corresponding state and state data */ - loadAgent.stateName shouldBe Idle + loadAgent.stateName shouldBe HandleInformation loadAgent.stateData match { - case ParticipantModelBaseStateData( - startDate, - endDate, - _, - services, - outputConfig, - additionalActivationTicks, - foreseenDataTicks, - _, - voltageValueStore, - resultValueStore, - requestValueStore, - _, - _, - _, + case CollectRegistrationConfirmMessages( + ParticipantModelBaseStateData( + startDate, + endDate, + _, + services, + outputConfig, + additionalActivationTicks, + foreseenDataTicks, + _, + voltageValueStore, + resultValueStore, + requestValueStore, + _, + _, + _, + ), + awaitRegistrationResponsesFrom, + foreseenNextDataTicks, ) => /* Base state data */ startDate shouldBe simulationStartDate endDate shouldBe simulationEndDate - services shouldBe Iterable.empty - outputConfig shouldBe defaultOutputConfig - additionalActivationTicks - .corresponds(Seq(900L, 1800L, 2700L, 3600L))(_ == _) shouldBe true + services shouldBe Iterable( + ActorLoadProfileService(loadProfileService.ref) + ) + outputConfig shouldBe NotifierConfig( + simulationResultInfo = false, + powerRequestReply = false, + flexResult = false, + ) + additionalActivationTicks shouldBe empty foreseenDataTicks shouldBe Map.empty voltageValueStore shouldBe ValueStore( resolution, SortedMap(0L -> Each(1.0)), ) resultValueStore shouldBe ValueStore(resolution) - requestValueStore shouldBe ValueStore[ComplexPower]( - resolution + requestValueStore shouldBe ValueStore[ComplexPower](resolution) + + /* Additional information */ + awaitRegistrationResponsesFrom shouldBe Iterable( + loadProfileService.ref + ) + foreseenNextDataTicks shouldBe Map.empty + case _ => + fail( + s"Did not find expected state data $CollectRegistrationConfirmMessages, but ${loadAgent.stateData}" + ) + } + + /* Reply, that registration was successful */ + loadProfileService.send( + loadAgent, + RegistrationSuccessfulMessage(loadProfileService.ref, Some(0L)), + ) + + /* Expect a completion notification */ + scheduler.expectMsg(Completion(loadAgent.toTyped, Some(0))) + + /* ... as well as corresponding state and state data */ + loadAgent.stateName shouldBe Idle + loadAgent.stateData match { + case baseStateData: ParticipantModelBaseStateData[_, _, _, _] => + /* Only check the awaited next data ticks, as the rest has yet been checked */ + baseStateData.foreseenDataTicks shouldBe Map( + loadProfileService.ref -> Some(0L) ) case _ => fail( @@ -243,6 +359,15 @@ class LoadAgentProfileModelCalculationSpec RegistrationFailedMessage(primaryServiceProxy.ref), ) + /* Expect a registration message */ + loadProfileService.expectMsg( + RegisterForLoadProfileService(BdewStandardLoadProfile.H0) + ) + loadProfileService.send( + loadAgent, + RegistrationSuccessfulMessage(loadProfileService.ref, Some(900L)), + ) + /* I'm not interested in the content of the Completion */ scheduler.expectMsgType[Completion] @@ -299,19 +424,66 @@ class LoadAgentProfileModelCalculationSpec RegistrationFailedMessage(primaryServiceProxy.ref), ) + /* I'm not interested in the content of the RegistrationMessage */ + loadProfileService.expectMsgType[RegisterForLoadProfileService] + loadProfileService.send( + loadAgent, + RegistrationSuccessfulMessage(loadProfileService.ref, Some(0L)), + ) + /* I am not interested in the Completion */ scheduler.expectMsgType[Completion] awaitAssert(loadAgent.stateName shouldBe Idle) /* State data is tested in another test */ + /* Send out an activity start trigger */ scheduler.send(loadAgent, Activation(0)) + /* Find yourself in appropriate state with state data */ + loadAgent.stateName shouldBe HandleInformation + loadAgent.stateData match { + case DataCollectionStateData( + baseStateData: ParticipantModelBaseStateData[_, _, _, _], + expectedSenders, + isYetTriggered, + ) => + /* The next data tick is already registered */ + baseStateData.foreseenDataTicks shouldBe Map( + loadProfileService.ref -> Some(0L) + ) + + /* The yet sent data is also registered */ + expectedSenders shouldBe Map(loadProfileService.ref -> None) + + /* It is yet triggered */ + isYetTriggered shouldBe true + case _ => + fail( + s"Did not find expected state data $DataCollectionStateData, but ${loadAgent.stateData}" + ) + } + + /* Providing the awaited data will lead to the foreseen transitions */ + val loadProfileData = LoadProfileData( + Watts(84d), + Watts(268.6d), + ) + + loadProfileService.send( + loadAgent, + ProvideLoadProfileValue( + 0L, + loadProfileService.ref, + loadProfileData, + Some(900L), + ), + ) + /* Expect confirmation */ scheduler.expectMsg(Completion(loadAgent.toTyped, Some(900))) /* Intermediate transitions and states cannot be tested, as the agents triggers itself * too fast */ - awaitAssert(loadAgent.stateName shouldBe Idle) inside(loadAgent.stateData) { case baseStateData: ParticipantModelBaseStateData[_, _, _, _] => @@ -351,15 +523,59 @@ class LoadAgentProfileModelCalculationSpec RegistrationFailedMessage(primaryServiceProxy.ref), ) + /* I'm not interested in the content of the RegistrationMessage */ + loadProfileService.expectMsgType[RegisterForLoadProfileService] + loadProfileService.send( + loadAgent, + RegistrationSuccessfulMessage(loadProfileService.ref, Some(0L)), + ) + scheduler.expectMsg(Completion(loadAgent.toTyped, Some(0))) + /* Send out the three data points */ + /* ... for tick 0 */ + loadProfileService.send( + loadAgent, + ProvideLoadProfileValue( + 0L, + loadProfileService.ref, + LoadProfileData(Watts(84d), Watts(268.6d)), + Some(900L), + ), + ) + /* Trigger the data generation in tick 0, 900, 1800 */ scheduler.send(loadAgent, Activation(0)) scheduler.expectMsg(Completion(loadAgent.toTyped, Some(900))) + /* ... for tick 900 */ + loadProfileService.send( + loadAgent, + ProvideLoadProfileValue( + 900L, + loadProfileService.ref, + LoadProfileData(Watts(75.5d), Watts(268.6d)), + Some(1800L), + ), + ) + scheduler.send(loadAgent, Activation(900)) scheduler.expectMsg(Completion(loadAgent.toTyped, Some(1800))) + /* ... for tick 1800 */ + loadProfileService.send( + loadAgent, + ProvideLoadProfileValue( + 1800L, + loadProfileService.ref, + LoadProfileData(Watts(68.2d), Watts(268.6d)), + None, + ), + ) + + scheduler.send(loadAgent, Activation(2700)) + scheduler.expectMsg(Completion(loadAgent.toTyped)) + awaitAssert(loadAgent.stateName shouldBe Idle) /* Ask the agent for average power in tick 1800 */ diff --git a/src/test/scala/edu/ie3/simona/config/ConfigFailFastSpec.scala b/src/test/scala/edu/ie3/simona/config/ConfigFailFastSpec.scala index f86b0b47c0..4c0e1d42ce 100644 --- a/src/test/scala/edu/ie3/simona/config/ConfigFailFastSpec.scala +++ b/src/test/scala/edu/ie3/simona/config/ConfigFailFastSpec.scala @@ -7,7 +7,7 @@ package edu.ie3.simona.config import com.typesafe.config.ConfigFactory -import edu.ie3.simona.config.SimonaConfig.Simona.Input.Weather.Datasource +import edu.ie3.simona.config.SimonaConfig.Simona.Input.{Loadprofile, Weather} import edu.ie3.simona.config.SimonaConfig.Simona.Input.Weather.Datasource.{ CoordinateSource, SampleParams, @@ -1039,6 +1039,35 @@ class ConfigFailFastSpec extends UnitSpec with ConfigTestData { /* Checking of primary source configuration is delegated to the specific actor. Tests are placed there */ + "Checking load profile data sources" should { + val checkLoadProfileDataSource = + PrivateMethod[Unit](Symbol("checkLoadProfileDataSource")) + + val csv: BaseCsvParams = + BaseCsvParams(",", "input", isHierarchic = false) + + val loadProfileDataSource = Loadprofile.Datasource( + None, + None, + ) + + "detect too many sources" in { + val tooManySources = loadProfileDataSource.copy( + csvParams = Some(csv), + sqlParams = + Some(Loadprofile.Datasource.SqlParams("", "", "", "", "")), + ) + + intercept[InvalidConfigParameterException] { + ConfigFailFast invokePrivate checkLoadProfileDataSource( + tooManySources + ) + }.getMessage should startWith( + "Multiple load profile sources defined:" + ) + } + } + "Checking weather data sources" should { val checkWeatherDataSource = PrivateMethod[Unit](Symbol("checkWeatherDataSource")) @@ -1047,13 +1076,12 @@ class ConfigFailFastSpec extends UnitSpec with ConfigTestData { BaseCsvParams(",", "input", isHierarchic = false) val sample = new SampleParams(true) - val weatherDataSource = Datasource( + val weatherDataSource = Weather.Datasource( CoordinateSource( None, "icon", Some( - SimonaConfig.Simona.Input.Weather.Datasource.CoordinateSource - .SampleParams(true) + Weather.Datasource.CoordinateSource.SampleParams(true) ), None, ), diff --git a/src/test/scala/edu/ie3/simona/model/participant/load/LoadModelScalingSpec.scala b/src/test/scala/edu/ie3/simona/model/participant/load/LoadModelScalingSpec.scala index c791664bd5..8d0109454f 100644 --- a/src/test/scala/edu/ie3/simona/model/participant/load/LoadModelScalingSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/participant/load/LoadModelScalingSpec.scala @@ -10,7 +10,7 @@ import edu.ie3.datamodel.models.OperationTime import edu.ie3.datamodel.models.input.system.LoadInput import edu.ie3.datamodel.models.input.system.characteristic.CosPhiFixed import edu.ie3.datamodel.models.input.{NodeInput, OperatorInput} -import edu.ie3.datamodel.models.profile.BdewStandardLoadProfile +import edu.ie3.datamodel.models.profile.{BdewStandardLoadProfile, LoadProfile} import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils import edu.ie3.simona.model.SystemComponent import edu.ie3.simona.model.participant.CalcRelevantData.LoadRelevantData @@ -19,8 +19,7 @@ import edu.ie3.simona.model.participant.load.LoadReference.{ ActivePower, EnergyConsumption, } -import edu.ie3.simona.model.participant.load.profile.ProfileLoadModel -import edu.ie3.simona.model.participant.load.random.RandomLoadModel +import edu.ie3.simona.service.load.LoadProfileStore import edu.ie3.simona.test.common.UnitSpec import edu.ie3.util.TimeUtil import edu.ie3.util.quantities.PowerSystemUnits @@ -35,6 +34,8 @@ import java.time.temporal.ChronoUnit import java.util.UUID class LoadModelScalingSpec extends UnitSpec with TableDrivenPropertyChecks { + // necessary to init and load psdm build in profiles + private val loadProfileStore = LoadProfileStore() "Testing correct scaling of load models" when { val simulationStartDate = @@ -100,11 +101,13 @@ class LoadModelScalingSpec extends UnitSpec with TableDrivenPropertyChecks { * are officially permissible. But, as we currently do not take (bank) holidays into account, we cannot reach * this accuracy. */ - calculateEnergyDiffForYear( + val x = calculateEnergyDiffForYear( model, simulationStartDate, targetEnergyConsumption, - ) should be < Percent(2d) + ) + + x should be < Percent(2d) } } @@ -340,8 +343,19 @@ class LoadModelScalingSpec extends UnitSpec with TableDrivenPropertyChecks { model: LoadModel[C] ): ZonedDateTime => C = model match { - case _: RandomLoadModel => RandomLoadModel.RandomRelevantData - case _: ProfileLoadModel => ProfileLoadModel.ProfileRelevantData + case _: RandomLoadModel => + time => + loadProfileStore + .entry(time, LoadProfile.RandomLoadProfile.RANDOM_LOAD_PROFILE) + .map(RandomLoadModel.RandomRelevantData) + .getOrElse(fail("No value found for random load model!")) + case loadModel: ProfileLoadModel => + time => { + val (averagePower, maxPower) = loadProfileStore + .valueOptions(time, loadModel.loadProfile) + .getOrElse(fail("No values found for average and max power here!")) + ProfileLoadModel.ProfileRelevantData(averagePower, maxPower) + } } def getRelativeDifference[Q <: Quantity[Q]]( diff --git a/src/test/scala/edu/ie3/simona/model/participant/load/LoadModelSpec.scala b/src/test/scala/edu/ie3/simona/model/participant/load/LoadModelSpec.scala index cca01b7904..3add6222a0 100644 --- a/src/test/scala/edu/ie3/simona/model/participant/load/LoadModelSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/participant/load/LoadModelSpec.scala @@ -7,8 +7,7 @@ package edu.ie3.simona.model.participant.load import edu.ie3.simona.model.participant.control.QControl -import edu.ie3.simona.model.participant.load.profile.ProfileLoadModel -import edu.ie3.simona.model.participant.load.random.RandomLoadModel +import edu.ie3.simona.service.load.LoadProfileStore import edu.ie3.simona.test.common.UnitSpec import edu.ie3.simona.test.common.input.LoadInputTestData import edu.ie3.util.scala.quantities.{ApparentPower, Voltamperes} @@ -26,6 +25,9 @@ class LoadModelSpec "The load model object" should { + // necessary to init and load psdm build in profiles + val loadProfileStore = LoadProfileStore() + "build a correct ProfileLoadModel from correct input" in { val params = Table( diff --git a/src/test/scala/edu/ie3/simona/model/participant/load/LoadProfileStoreSpec.scala b/src/test/scala/edu/ie3/simona/model/participant/load/LoadProfileStoreSpec.scala deleted file mode 100644 index 6b5d7ee5b2..0000000000 --- a/src/test/scala/edu/ie3/simona/model/participant/load/LoadProfileStoreSpec.scala +++ /dev/null @@ -1,151 +0,0 @@ -/* - * © 2020. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.simona.model.participant.load - -import com.typesafe.scalalogging.LazyLogging -import edu.ie3.datamodel.models.profile.BdewStandardLoadProfile._ -import edu.ie3.datamodel.models.profile.StandardLoadProfile -import edu.ie3.simona.model.participant.load.profile.{ - LoadProfileKey, - LoadProfileStore, - TypeDayProfile, -} -import edu.ie3.simona.test.common.UnitSpec -import edu.ie3.util.TimeUtil -import edu.ie3.util.scala.quantities.Voltamperes -import org.scalatest.PrivateMethodTester -import org.scalatest.prop.TableDrivenPropertyChecks -import squants.energy.{KilowattHours, Watts} -import squants.time.Minutes - -import java.io.InputStreamReader -import java.time.ZonedDateTime -import java.time.temporal.ChronoUnit - -class LoadProfileStoreSpec - extends UnitSpec - with PrivateMethodTester - with TableDrivenPropertyChecks - with LazyLogging { - - // Load a copy of the standard load profile. - // This file, in contrast to the working system load - // profile file, should not be altered. - val reader = new InputStreamReader( - this.getClass.getResourceAsStream("standard_load_profiles_test.csv") - ) - - val customStore: LoadProfileStore = LoadProfileStore(reader) - - "A LoadProfileStore" must { - "return the correct values based on a custom reader" in { - val params = - Table( - ("timestamp", "consumerType", "param"), - ( - "2019-04-01T05:00:00+02:00[Europe/Berlin]", - L0, - 55.6d, - ), // Weekday, transitional, 20th quarter-hour - ( - "2019-06-02T00:00:00+02:00[Europe/Berlin]", - G0, - 68.8d, - ), // Sunday, summer, 0th quarter-hour - ( - "2019-01-05T02:15:00+01:00[Europe/Berlin]", - H0, - 52.8d, - ), // Saturday, winter, 9th quarter-hour, 5th day -> dynamization factor 1.2473 - ( - "2019-07-21T01:00:00+02:00[Europe/Berlin]", - H0, - 58.1d, - ), // Sunday, summer, 4th quarter-hour, 202nd day -> dynamization factor 0.7847 - ) - - forAll(params) { - ( - timestamp: String, - loadProfile: StandardLoadProfile, - paramValue: Double, - ) => - val time = ZonedDateTime.parse(timestamp) - val param = Watts(paramValue) - - customStore.entry(time, loadProfile) shouldBe param - } - } - - "return correct max params based on a custom reader" in { - val maxParams = - Table( - ("profile", "maxParamValue"), - (H0, 268.6d), - (L0, 240.4d), - (G0, 240.4d), - ) - - forAll(maxParams) { - (loadProfile: StandardLoadProfile, maxParamValue: Double) => - val maxParam = Watts(maxParamValue) - - customStore.maxPower(loadProfile) shouldBe maxParam - } - } - - "have values, that lead to correct annual energy consumption" in { - /* Collect all available load profiles */ - val availableLoadProfiles: Set[StandardLoadProfile] = - (customStore invokePrivate PrivateMethod[ - Map[LoadProfileKey, TypeDayProfile] - ](Symbol("profileMap"))()).keySet.map(_.standardLoadProfile) - - /* List the expected annual energy consumption */ - val expectedEnergyConsumption: Map[StandardLoadProfile, squants.Energy] = - Map( - H0 -> KilowattHours(1000.0), - L0 -> KilowattHours(1002.0), - /* TODO: Check, if this is correct */ - G0 -> KilowattHours(1022.0), - ) - - /* Collect all available time steps in 2020 */ - val startDate = - TimeUtil.withDefaults.toZonedDateTime("2020-01-01T00:00:00Z") - val testDates = - Range(0, 35136).map(cnt => startDate.plus(cnt * 15, ChronoUnit.MINUTES)) - - /* Check every load profile */ - availableLoadProfiles.foreach(profile => { - /* Get expected value */ - val expected = expectedEnergyConsumption.get(profile) match { - case Some(expectedValue) => expectedValue - case None => - fail( - s"There is no expected annual energy consumption for '$profile'" - ) - } - - /* Sum up value throughout the year (2020 is a leap year) */ - val annualEnergy: squants.Energy = testDates - .foldLeft( - KilowattHours(0.0) - )((integrationState, dateTime) => { - val currentEnergy = customStore - .entry(dateTime, profile) * Minutes(15.0) - integrationState + currentEnergy - }) - - implicit val powerTolerance: squants.Energy = KilowattHours(1.0) - - /* Check the deviation against the expected value (deviation should be smaller than 1 kWh) */ - annualEnergy should approximate(expected) - }) - } - } -} diff --git a/src/test/scala/edu/ie3/simona/model/participant/load/ProfileLoadModelSpec.scala b/src/test/scala/edu/ie3/simona/model/participant/load/ProfileLoadModelSpec.scala index 0cd81c66b5..c181597a72 100644 --- a/src/test/scala/edu/ie3/simona/model/participant/load/ProfileLoadModelSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/participant/load/ProfileLoadModelSpec.scala @@ -17,7 +17,7 @@ import edu.ie3.simona.model.participant.load.LoadReference.{ ActivePower, EnergyConsumption, } -import edu.ie3.simona.model.participant.load.profile.ProfileLoadModel +import edu.ie3.simona.service.load.LoadProfileStore import edu.ie3.simona.test.common.UnitSpec import edu.ie3.util.TimeUtil import edu.ie3.util.quantities.PowerSystemUnits @@ -70,6 +70,9 @@ class ProfileLoadModelSpec extends UnitSpec with TableDrivenPropertyChecks { loadInput.getOperationTime, ) + // necessary to init and load psdm build in profiles + val loadProfileStore = LoadProfileStore() + "instantiating it" should { "deliver a proper model" in { forAll( diff --git a/src/test/scala/edu/ie3/simona/model/participant/load/RandomLoadModelSpec.scala b/src/test/scala/edu/ie3/simona/model/participant/load/RandomLoadModelSpec.scala index eb57409b0d..7bd05485a0 100644 --- a/src/test/scala/edu/ie3/simona/model/participant/load/RandomLoadModelSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/participant/load/RandomLoadModelSpec.scala @@ -19,10 +19,6 @@ import edu.ie3.simona.model.participant.load.LoadReference.{ ActivePower, EnergyConsumption, } -import edu.ie3.simona.model.participant.load.random.{ - RandomLoadModel, - RandomLoadParameters, -} import edu.ie3.simona.test.common.UnitSpec import edu.ie3.util.TimeUtil import edu.ie3.util.quantities.PowerSystemUnits @@ -98,51 +94,6 @@ class RandomLoadModelSpec extends UnitSpec with TableDrivenPropertyChecks { } } } - - "calculating results" should { - "deliver the correct distribution on request" in { - val dut = new RandomLoadModel( - loadInput.getUuid, - loadInput.getId, - foreSeenOperationInterval, - QControl.apply(loadInput.getqCharacteristics()), - Kilovoltamperes( - loadInput - .getsRated() - .to(PowerSystemUnits.KILOVOLTAMPERE) - .getValue - .doubleValue() - ), - loadInput.getCosPhiRated, - ActivePower(Watts(268.6)), - ) - /* Working day, 61st quarter-hour */ - val queryDate = - TimeUtil.withDefaults.toZonedDateTime("2019-07-19T15:21:00Z") - val expectedParams = new RandomLoadParameters( - 0.405802458524704, - 0.0671483352780342, - 0.0417016632854939, - ) - - /* First query leeds to generation of distribution */ - val getGevDistribution = - PrivateMethod[GeneralizedExtremeValueDistribution]( - Symbol("getGevDistribution") - ) - - def firstHit = dut invokePrivate getGevDistribution(queryDate) - - firstHit.getK shouldBe expectedParams.k - firstHit.getMu shouldBe expectedParams.my - firstHit.getSigma shouldBe expectedParams.sigma - - /* Second query is only look up in storage */ - def secondHit = dut invokePrivate getGevDistribution(queryDate) - - secondHit shouldBe firstHit - } - } } } diff --git a/src/test/scala/edu/ie3/simona/model/participant/load/RandomLoadParamStoreSpec.scala b/src/test/scala/edu/ie3/simona/model/participant/load/RandomLoadParamStoreSpec.scala deleted file mode 100644 index 5f214e0796..0000000000 --- a/src/test/scala/edu/ie3/simona/model/participant/load/RandomLoadParamStoreSpec.scala +++ /dev/null @@ -1,125 +0,0 @@ -/* - * © 2020. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.simona.model.participant.load - -import java.io.InputStreamReader - -import edu.ie3.simona.model.participant.load.random.{ - RandomLoadParamStore, - RandomLoadParameters, - TypeDayParameters, -} -import edu.ie3.simona.test.common.UnitSpec -import edu.ie3.util.TimeUtil -import org.scalatest.PrivateMethodTester -import org.scalatest.prop.TableDrivenPropertyChecks - -class RandomLoadParamStoreSpec - extends UnitSpec - with PrivateMethodTester - with TableDrivenPropertyChecks { - - "A random load parameter store" should { - "is able to build a correct descriptor tree" in { - val buildDescriptorTree = - PrivateMethod[Map[DayType.Value, Map[RandomLoadParameters.Value, Int]]]( - Symbol("buildDescriptorTree") - ) - val actual = RandomLoadParamStore invokePrivate buildDescriptorTree( - List( - "kSa", - "kSu", - "kWd", - "mySa", - "mySu", - "myWd", - "sigmaSa", - "sigmaSu", - "sigmaWd", - "quarterHour", - ) - ) - val expected = Map( - DayType.weekday -> Map( - RandomLoadParameters.K -> 2, - RandomLoadParameters.MY -> 5, - RandomLoadParameters.SIGMA -> 8, - ), - DayType.saturday -> Map( - RandomLoadParameters.K -> 0, - RandomLoadParameters.MY -> 3, - RandomLoadParameters.SIGMA -> 6, - ), - DayType.sunday -> Map( - RandomLoadParameters.K -> 1, - RandomLoadParameters.MY -> 4, - RandomLoadParameters.SIGMA -> 7, - ), - ) - - actual shouldBe expected - } - - "be instantiated correctly" in { - val reader: InputStreamReader = new InputStreamReader( - this.getClass - .getResourceAsStream("random_load_parameters_test.csv") - ) - val parameterMap = - RandomLoadParamStore(reader) invokePrivate PrivateMethod[ - Map[DayType.Value, TypeDayParameters] - ](Symbol("parameterMap"))() - parameterMap.size shouldBe 3 - } - - "return the correct parameters" in { - val reader: InputStreamReader = new InputStreamReader( - this.getClass - .getResourceAsStream("random_load_parameters_test.csv") - ) - val randomParameterStore = RandomLoadParamStore(reader) - - val params = - Table( - ("timestamp", "expected"), - ( - "2019-04-01T05:00:00Z", - RandomLoadParameters( - 0.146539300680161, - 0.0430354326963425, - 0.0201929099857807, - ), - ), // Weekday - ( - "2019-06-02T00:00:00Z", - RandomLoadParameters( - 0.295997023582459, - 0.0630703344941139, - 0.0370676517486572, - ), - ), // Sunday - ( - "2019-01-05T02:15:00Z", - RandomLoadParameters( - 0.132398754358292, - 0.0439879409968853, - 0.0208074823021889, - ), - ), // Saturday - ) - - forAll(params) { - ( - timestamp: String, - expected: RandomLoadParameters, - ) => - val time = TimeUtil.withDefaults.toZonedDateTime(timestamp) - randomParameterStore.parameters(time) shouldBe expected - } - } - } -} diff --git a/src/test/scala/edu/ie3/simona/model/participant/load/TypeDayParametersSpec.scala b/src/test/scala/edu/ie3/simona/model/participant/load/TypeDayParametersSpec.scala deleted file mode 100644 index 84f1d45e71..0000000000 --- a/src/test/scala/edu/ie3/simona/model/participant/load/TypeDayParametersSpec.scala +++ /dev/null @@ -1,56 +0,0 @@ -/* - * © 2020. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.simona.model.participant.load - -import edu.ie3.simona.model.participant.load.random.{ - RandomLoadParameters, - TypeDayParameters, -} -import edu.ie3.simona.test.common.UnitSpec -import edu.ie3.util.TimeUtil - -class TypeDayParametersSpec extends UnitSpec { - "A set of type day parameters" should { - "throw an exception, when it is meant to be instantiated with too less values" in { - val tooLessValues = Range(0, 100, 10) - .map(cnt => - RandomLoadParameters( - cnt.toDouble, - (cnt + 1).toDouble, - (cnt + 2).toDouble, - ) - ) - .toArray - intercept[IllegalArgumentException] { - TypeDayParameters(tooLessValues) - }.getMessage shouldBe "You may only instantiate type day parameters with 96 values. Apparent: 10." - } - - "be able to return the correct values on request" in { - val typeDayParameters = TypeDayParameters( - Range(0, 960, 10) - .map(cnt => - RandomLoadParameters( - cnt.toDouble, - (cnt + 1).toDouble, - (cnt + 2).toDouble, - ) - ) - .toArray - ) - typeDayParameters.getQuarterHourParameters( - TimeUtil.withDefaults.toZonedDateTime("2020-04-22T00:15:00Z") - ) shouldBe RandomLoadParameters(10d, 11d, 12d) - typeDayParameters.getQuarterHourParameters( - TimeUtil.withDefaults.toZonedDateTime("2020-04-22T14:30:00Z") - ) shouldBe RandomLoadParameters(580d, 581d, 582d) - typeDayParameters.getQuarterHourParameters( - TimeUtil.withDefaults.toZonedDateTime("2020-04-22T19:00:00Z") - ) shouldBe RandomLoadParameters(760d, 761d, 762d) - } - } -} diff --git a/src/test/scala/edu/ie3/simona/model/participant/load/TypeDayProfileSpec.scala b/src/test/scala/edu/ie3/simona/model/participant/load/TypeDayProfileSpec.scala deleted file mode 100644 index 4e94953967..0000000000 --- a/src/test/scala/edu/ie3/simona/model/participant/load/TypeDayProfileSpec.scala +++ /dev/null @@ -1,41 +0,0 @@ -/* - * © 2020. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ - -package edu.ie3.simona.model.participant.load - -import edu.ie3.simona.model.participant.load.profile.TypeDayProfile -import edu.ie3.simona.test.common.UnitSpec -import edu.ie3.util.TimeUtil - -class TypeDayProfileSpec extends UnitSpec { - "A type day profile" should { - "throw an exception, when it is meant to be instantiated with too less values" in { - val tooLessValues = Range(0, 10).map(_.toDouble).toArray - intercept[IllegalArgumentException] { - TypeDayProfile(tooLessValues) - }.getMessage shouldBe "You may only instantiate type day parameters with 96 values." - } - - "be able to return the correct values on request" in { - val typeDayProfile = TypeDayProfile(Range(0, 96).map(_.toDouble).toArray) - typeDayProfile.getQuarterHourEnergy( - TimeUtil.withDefaults.toZonedDateTime("2020-04-22T00:15:00Z") - ) shouldBe 1d - typeDayProfile.getQuarterHourEnergy( - TimeUtil.withDefaults.toZonedDateTime("2020-04-22T14:30:00Z") - ) shouldBe 58d - typeDayProfile.getQuarterHourEnergy( - TimeUtil.withDefaults.toZonedDateTime("2020-04-22T19:00:00Z") - ) shouldBe 76d - } - - "returns the correct maximum value" in { - TypeDayProfile( - Range(0, 96).map(_.toDouble).toArray - ).getMaxValue shouldBe 95d - } - } -} diff --git a/src/test/scala/edu/ie3/simona/service/load/LoadProfileServiceSpec.scala b/src/test/scala/edu/ie3/simona/service/load/LoadProfileServiceSpec.scala new file mode 100644 index 0000000000..43b0daef41 --- /dev/null +++ b/src/test/scala/edu/ie3/simona/service/load/LoadProfileServiceSpec.scala @@ -0,0 +1,204 @@ +/* + * © 2024. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.simona.service.load + +import com.typesafe.config.ConfigFactory +import com.typesafe.scalalogging.LazyLogging +import edu.ie3.datamodel.models.profile.{BdewStandardLoadProfile, LoadProfile} +import edu.ie3.simona.config.SimonaConfig +import edu.ie3.simona.ontology.messages.Activation +import edu.ie3.simona.ontology.messages.SchedulerMessage.{ + Completion, + ScheduleActivation, +} +import edu.ie3.simona.ontology.messages.services.LoadProfileMessage.{ + LoadProfileData, + ProvideLoadProfileValue, + RegisterForLoadProfileService, +} +import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResponseMessage.{ + RegistrationFailedMessage, + RegistrationSuccessfulMessage, +} +import edu.ie3.simona.scheduler.ScheduleLock +import edu.ie3.simona.service.SimonaService +import edu.ie3.simona.service.load.LoadProfileService.InitLoadProfileServiceStateData +import edu.ie3.simona.test.common.{ + ConfigTestData, + TestKitWithShutdown, + TestSpawnerClassic, +} +import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK +import edu.ie3.util.TimeUtil +import org.apache.pekko.actor.ActorSystem +import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps +import org.apache.pekko.testkit.{ + EventFilter, + ImplicitSender, + TestActorRef, + TestProbe, +} +import org.scalatest.PrivateMethodTester +import org.scalatest.wordspec.AnyWordSpecLike +import squants.energy.Watts + +class LoadProfileServiceSpec + extends TestKitWithShutdown( + ActorSystem( + "LoadProfileServiceSpec", + ConfigFactory + .parseString(""" + |pekko.loggers = ["org.apache.pekko.testkit.TestEventListener"] + |pekko.loglevel = "INFO" + """.stripMargin), + ) + ) + with ImplicitSender + with AnyWordSpecLike + with PrivateMethodTester + with LazyLogging + with ConfigTestData + with TestSpawnerClassic { + + // setup config for scheduler + private val config = ConfigFactory + .parseString(s""" + simona.time.startDateTime = "2011-01-01T00:00:00Z" + simona.time.endDateTime = "2011-01-01T01:00:00Z" + simona.time.schedulerReadyCheckWindow = 900 + simona.input.grid.datasource.id = "csv" + simona.input.grid.datasource.csvParams.folderPath = "netdata" + simona.input.grid.datasource.csvParams.csvSep ="," + simona.input.grid.datatarget.id = "csv" + simona.powerflow.maxSweepPowerDeviation = 1E-5 // the maximum allowed deviation in power between two sweeps, before overall convergence is assumed + simona.powerflow.newtonraphson.epsilon = [1E-12] + simona.powerflow.newtonraphson.iterations = 50 + simona.simulationName = "ConfigTestDataSimulation" + simona.gridConfig.refSystems = [] + """) + .resolve() + .withFallback(typesafeConfig) + override protected val simonaConfig: SimonaConfig = SimonaConfig(config) + // setup values + private val invalidLoadProfile: LoadProfile = new LoadProfile { + override def getKey: String = "invalidLoadProfile" + } + private val validLoadProfile: LoadProfile = BdewStandardLoadProfile.G0 + + private val scheduler = TestProbe("scheduler") + + // build the load profile service + private val loadProfileActor: TestActorRef[LoadProfileService] = TestActorRef( + new LoadProfileService( + scheduler.ref, + TimeUtil.withDefaults.toZonedDateTime( + simonaConfig.simona.time.startDateTime + ), + TimeUtil.withDefaults.toZonedDateTime( + simonaConfig.simona.time.endDateTime + ), + ) + ) + + "A load profile service" must { + + "receive correct completion message after initialisation" in { + val key = + ScheduleLock.singleKey(TSpawner, scheduler.ref.toTyped, INIT_SIM_TICK) + scheduler.expectMsgType[ScheduleActivation] // lock activation scheduled + + scheduler.send( + loadProfileActor, + SimonaService.Create( + InitLoadProfileServiceStateData( + simonaConfig.simona.input.loadprofile.datasource + ), + key, + ), + ) + scheduler.expectMsg( + ScheduleActivation(loadProfileActor.toTyped, INIT_SIM_TICK, Some(key)) + ) + + scheduler.send(loadProfileActor, Activation(INIT_SIM_TICK)) + scheduler.expectMsg(Completion(loadProfileActor.toTyped, Some(0))) + } + + "announce failed load profile registration on invalid coordinate" in { + EventFilter + .error( + pattern = + "\\[.*] Unable to obtain necessary information to register for load profile 'invalidLoadProfile'.", + occurrences = 1, + ) + .intercept { + loadProfileActor ! RegisterForLoadProfileService(invalidLoadProfile) + } + + expectMsg(RegistrationFailedMessage(loadProfileActor)) + } + + "announce, that a valid load profile is registered" in { + /* The successful registration stems from the test above */ + loadProfileActor ! RegisterForLoadProfileService(validLoadProfile) + + expectMsg(RegistrationSuccessfulMessage(loadProfileActor.ref, Some(0L))) + } + + "recognize, that a valid coordinate yet is registered" in { + /* The successful registration stems from the test above */ + EventFilter + .warning( + pattern = "Sending actor Actor\\[.*] is already registered", + occurrences = 1, + ) + .intercept { + loadProfileActor ! RegisterForLoadProfileService(validLoadProfile) + } + expectNoMessage() + } + + "send out correct load profile information upon activity start trigger and request the triggering for the next tick" in { + /* Send out an activity start trigger as the scheduler */ + scheduler.send(loadProfileActor, Activation(0)) + + scheduler.expectMsg(Completion(loadProfileActor.toTyped, Some(900))) + + expectMsg( + ProvideLoadProfileValue( + 0, + loadProfileActor, + LoadProfileData( + Watts(70d), + Watts(240.4), + ), + Some(900L), + ) + ) + + } + + "sends out correct load profile information when triggered again and does not as for triggering, if the end is reached" in { + /* Send out an activity start trigger as the scheduler */ + scheduler.send(loadProfileActor, Activation(900L)) + + scheduler.expectMsg(Completion(loadProfileActor.toTyped, Some(1800L))) + + expectMsg( + ProvideLoadProfileValue( + 900L, + loadProfileActor, + LoadProfileData( + Watts(73d), + Watts(240.4), + ), + Some(1800L), + ) + ) + } + } +} diff --git a/src/test/scala/edu/ie3/simona/service/load/LoadProfileStoreSpec.scala b/src/test/scala/edu/ie3/simona/service/load/LoadProfileStoreSpec.scala new file mode 100644 index 0000000000..b13a1867e5 --- /dev/null +++ b/src/test/scala/edu/ie3/simona/service/load/LoadProfileStoreSpec.scala @@ -0,0 +1,81 @@ +/* + * © 2024. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ + +package edu.ie3.simona.service.load + +import edu.ie3.datamodel.io.source.LoadProfileSource +import edu.ie3.datamodel.models.profile.LoadProfile.{ + DefaultLoadProfiles, + RandomLoadProfile, +} +import edu.ie3.datamodel.models.profile.{BdewStandardLoadProfile, LoadProfile} +import edu.ie3.simona.test.common.UnitSpec +import org.mockito.Mockito.when +import org.scalatestplus.mockito.MockitoSugar.mock + +import java.util.Optional +import scala.util.Try + +class LoadProfileStoreSpec extends UnitSpec { + + private val loadProfile1 = new LoadProfile { + override def getKey: String = "lp1" + } + + private val loadProfile2 = new LoadProfile { + override def getKey: String = "lp2" + } + + "The load profile store" should { + + "be create correctly" in { + val buildInsStore = LoadProfileStore() + + BdewStandardLoadProfile + .values() + .foreach(profile => + Try(buildInsStore.valueProvider(profile)).isSuccess shouldBe true + ) + + Try( + buildInsStore.valueProvider(RandomLoadProfile.RANDOM_LOAD_PROFILE) + ).isSuccess shouldBe true + + Try( + buildInsStore.valueProvider(DefaultLoadProfiles.NO_LOAD_PROFILE) + ).isFailure shouldBe true + + } + + "return known load profiles correctly" in { + val buildInsStore = LoadProfileStore() + + val buildInsProfiles = BdewStandardLoadProfile.values().toSet ++ Set( + LoadProfile.RandomLoadProfile.RANDOM_LOAD_PROFILE + ) + + val store = LoadProfileStore( + Map( + loadProfile1 -> mockSource, + loadProfile2 -> mockSource, + ) + ) + + buildInsStore.getKnownProfiles shouldBe buildInsProfiles + store.getKnownProfiles shouldBe buildInsProfiles ++ Set( + loadProfile1, + loadProfile2, + ) + } + + } + + private def mockSource: LoadProfileSource[_, _] = { + val source = mock[LoadProfileSource[_, _]] + when(source.getMaxPower).thenReturn(Optional.empty()) + source + } +} diff --git a/src/test/scala/edu/ie3/simona/service/weather/WeatherSourceWrapperSpec.scala b/src/test/scala/edu/ie3/simona/service/weather/WeatherSourceWrapperSpec.scala index 3c937e0976..1306c43061 100644 --- a/src/test/scala/edu/ie3/simona/service/weather/WeatherSourceWrapperSpec.scala +++ b/src/test/scala/edu/ie3/simona/service/weather/WeatherSourceWrapperSpec.scala @@ -371,6 +371,10 @@ object WeatherSourceWrapperSpec { case None => Optional.empty() } } + + override def getTimeKeysAfter( + zonedDateTime: ZonedDateTime + ): util.Map[Point, util.List[ZonedDateTime]] = ??? } /** Prepare test data for WeightSum-related tests diff --git a/src/test/scala/edu/ie3/simona/sim/SimonaSimSpec.scala b/src/test/scala/edu/ie3/simona/sim/SimonaSimSpec.scala index 9d1140be17..24417eeea9 100644 --- a/src/test/scala/edu/ie3/simona/sim/SimonaSimSpec.scala +++ b/src/test/scala/edu/ie3/simona/sim/SimonaSimSpec.scala @@ -429,6 +429,12 @@ object SimonaSimSpec { ): ClassicRef = context.spawn(empty, uniqueName("weatherService")).toClassic + override def loadProfileService( + context: ActorContext[_], + scheduler: ActorRef[SchedulerMessage], + ): ClassicRef = + context.spawn(empty, uniqueName("loadProfileService")).toClassic + override def timeAdvancer( context: ActorContext[_], simulation: ActorRef[SimonaSim.SimulationEnded.type], diff --git a/src/test/scala/edu/ie3/simona/sim/setup/SimonaSetupSpec.scala b/src/test/scala/edu/ie3/simona/sim/setup/SimonaSetupSpec.scala index 0428ed765b..503c884006 100644 --- a/src/test/scala/edu/ie3/simona/sim/setup/SimonaSetupSpec.scala +++ b/src/test/scala/edu/ie3/simona/sim/setup/SimonaSetupSpec.scala @@ -57,6 +57,11 @@ class SimonaSetupSpec extends UnitSpec with SimonaSetup with SubGridGateMokka { scheduler: ActorRef[SchedulerMessage], ): ClassicRef = throw new NotImplementedException("This is a dummy setup") + override def loadProfileService( + context: ActorContext[_], + scheduler: ActorRef[SchedulerMessage], + ): ClassicRef = throw new NotImplementedException("This is a dummy setup") + override def extSimulations( context: ActorContext[_], scheduler: ActorRef[SchedulerMessage],