Python-Fuzzylogic/fuzzylogic

Information

bbartling opened this issue · 46 comments

Hello,

I am new to fuzzy logic curious on how it could possibility fit an application I am experimenting with in the heating ventilation & air conditioning industry. Could I ever describe my process of linear control with PID here to see if fuzzy control would be applicable?

Apologize if a git issue would not be appropriate method!

Thank you for anytime you have in response.

Hi!

Sure, I don't see why not. One of the first applications of fuzzy logic was the automation of trains in Japan ( see https://en.wikipedia.org/wiki/Fuzzy_logic#Early_applications ), just as an example of what can be achieved with it in terms of automation. Temperature, humidity, speed of ventilation etc. are all easily mapped to the unit interval [0, 1], so you only need to think of your predicates - at what point do you consider a temperature "hot", when is the air "too dry" etc. but that are all questions you need to answer anyway as part of your specification.

I guess the main difference to a PID is that you are much more free in how you define your mappings, response triggers etc. - using fuzzylogic.functions and fuzzylogic.hedges, you can easily model non-linear relations, which you, as an engineer, probably would first try to simplify and approximate by (perhaps piece-wise) linear relations, which, in the end, is more complicated for you, as a human, to work with rather than letting the machine do all the heavy lifting in the background.

So, a PID can probably do the job if your use case is rather simple. If your system is complex enough, you might want to give fuzzy logic a try and let it help you abstract away those nonlinear functions for you.

I hope that helps. I would be very curious to hear if and how you want to apply fuzzy logic - in general or maybe even my little project.

Hi!

Thanks for the quick response!

So this is the basics of it with PID. For every 10% increment upward from 0% of the PID calculation the A/C system in a building gets turned down a notch to prevent the electrical usage from getting too high. (prevent high demand charges on the utility bills) Some of the acronym's below in the screenshot not make sense unless your a HVAC controls technician, sorry.

image

The PID control variable is whole building metering (kW) and the PID threshold would be a threshold not to exceed which is different building to building. The buildings react really slow. So its not crucial about very fast calculations or anything. I was envisioning the PID cycling time of like once every 5 minutes or possibly down to 60 seconds.

The paragraph describing how to rank which zones to close down (turn off the A/C) I am still trying to dream up a method for fuzzy logic rules. Basically each zone (30 zones) can get ranked depending on how much air flow and how far they are off from setpoint. This is also constantly changing as the sun travels from East to West side of the building zones with direct sunlight have more heat gain. So my idea was to somehow rank which zones to close first when power consumption gets too high.

For example, turn down A/C in zones where there is high air flow and zone temperature is at setpoint. Next (as PID would continue to ramp upwards) further select zones with high air flow that are only a little over setpoint. AND finally as PID would continue to calculate select all zones even zones with low air flow and ones that are too warm/over setpoint.

Hopefully that makes sense! I am still trying to soak in the fuzzy theory so any tips / strategy to think about greatly appreciated...

I am still trying wrap my head around what I would need to know (incorporate the physical attributes of the A/C system sizing?) for this when fuzzy logic would be applied to an A/C system application to prevent building electricity from getting too high:

The Idea
So, the idea is to have four main parts that work together: domains, sets, functions and rules.

What would be the domain?

You start modelling your system by defining your domain of interest. Then you think about where your interesting points are in that domain and look for a function that might do what you want.

A domain is a measurable dimension, like for instance a thermometer that measures from -50 to 80 °C with a resolution of 0.2 °C - that would be Domain(-50, 80, res=0.2) or a ventilator you control in terms of rotations per second, controllable in steps of 100 Hertz maybe would be a Domain(0, 1200, res=100), something like that.

According to your schema, you might need domains for measured airflow and temperature. Depending on if and how you want to employ your PID, you could define a domain for its input or output and/or have domains for the pressure and lighting systems, too.

The domains define the range of possible states your actual, physical system can be in.
From there, you define a set with its function to map actual, measured values onto [0,1].

For instance, coming from your schema, one could do

PID = Domain("PID", 0,100, res=1)
PID.ahu_power = S(80, 90)  # means 1 for values below 80 and 0 for values above 90, linear gradient between 80 and 90
PID.clg_setpoint_raise = R(90, 100)  # means 0 for values below 90 up to 1 at 100
PID.shut_off_lighting = R(99, 100)  # hmm.. maybe a "step" function would be useful for this?

Now you could take the output of your PID and feed it to the PID-domain above, which will give you a dict with the results of the three sets, you can then use to send commands to your devices. You could add arbitrarily complex logic and chain sets, but you always get your dict with values of 0 to 1 back.

Edit: The defuzzification step (the mapping back from 0 to 1 to actual values within a domain) in my code is a bit neglected because I never really needed it for my own stuff, sorry. There are different solutions to this, the most common probably is to apply the center of gravity method. I'd try to apply https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.center_of_mass.html, which should 'just work'.

Could I ever pick your mind more about how I could use fuzzylogic without the PID? Sorry a lot of information here on an odd ball application! This material you present is super cool thank you so much for taking the time to respond to me.... I am really hoping to know more...! Also sorry for how long this message is turning out.

So just for simplistic ideas to present, I think I can explain best with making up some data for now but in real life scenorio it would be pulled on 5 or 10 minute intervals from the HVAC control system. Each interval of data pulled I was hoping for some logical command to be issued maybe through fuzzy logic?

This code below will create a Pandas DataFrame of 2 columns one column integer called Air_Flow and the other column of floats called Stp_Deviation. So when the cooling system is running in a typical larger commercial building with a VAV type of system each zone will always have an air flow measurement and the deviation from zone temperature and zone temperature setpoint is always something easy to calculate. In the video link, the guy talks about individual zones with a controller and an air damper. This is the basic idea on how to save power consumption for the building its to close off the air (through a small round actuator powered damper on a VAV) to a zone which ultimately takes the load off of the mechanical cooling compressors.
image

So the code output could be something very generalized for typical buildings if this building had 10 zones. Hopefully more realistic data generation in the future but the numbers presented below are within the typical ranges of real life data. These numbers are always slowly changing too throughout a typical day as the dynamics of the system are constantly changing all dependent on required cooling loads.

import random
import numpy as np
import pandas as pd

data = np.random.randint(0,2000,size=15)
df = pd.DataFrame(data, columns=['Air_Flow'])


#generate random floats for df2
randomFloatList = []
# Set a length of the list to length of pandas df1
for i in range(0, len(df)):
    # any random float between 5.50 to 50.50
    x = round(random.uniform(-3.50, 5.50), 2)
    randomFloatList.append(x)

#setpointDeviation = ZoneTemp - ZoneSetpoint
df2 = pd.DataFrame(randomFloatList,columns=['Stp_Deviation'])


combined = df.join(df2)
print(combined)

print(combined.sort_values(['Air_Flow','Stp_Deviation'], ascending=[False, False]))

print(df2) output is shown below, and if I were analyzing this data on a live system I could quickly see which zones are under or over setpoint. For example, #5 below with deviation of 5.41 could potentially be some very unhappy people in the space if the zone temperature was 5+ degrees over setpoint. Its too hot and the air flow is relatively low.

    Air_Flow  Stp_Deviation
0       1478          -1.81
1       1562           0.40
2        145          -1.59
3        783           4.07
4        211           5.41
5        565           3.33
6       1940          -0.58
7       1877           5.03
8       1670          -1.72
9        109           1.01
10       195          -0.19
11      1318           0.65
12       605           0.92
13       949          -1.69
14      1806           3.00

If I print combined which is a sorted variation of the same data (below) I can quickly see which zones have the most air flow and have the least deviation from setpoint. The idea with PID was to select zones to shut off the A/C in that would have the largest impact on the power consumption which I am attempting to do that below. If using PID, when the calculation in % starts to rise up as building power consumption nears a setpoint, used the sorted list of zones that would have the most impact. Start with the top and little by little zone by zone start closing the air damper for the zones that would have the most impact to lower electricity consumption.

    Air_Flow  Stp_Deviation
6       1940          -0.58
7       1877           5.03
14      1806           3.00
8       1670          -1.72
1       1562           0.40
0       1478          -1.81
11      1318           0.65
13       949          -1.69
3        783           4.07
12       605           0.92
5        565           3.33
4        211           5.41
10       195          -0.19
2        145          -1.59
9        109           1.01

A lot of info here sorry this message is really long.

Domains: (?)

  • For air flow imperial units would be anything from 0-2000 (CFM)

  • zone setpoint deviation would be anything from -5 to 5 (°F) , anything outside this range something is wrong with the sensor or controls.

  • Building power consumption for this building would be 0 - 125 kW. (typical summer daily load plot shown below) Setpoint would be something around 75 kW as the building power rises above the yellow line below on some random day in July 2019.

image

Anytime response greatly appreciated!!

Would whole building power be the domain for my application? The middle layer between fuzzification and defuzzication be all my IF ELSE statements about air flow and zone temp deviations?

Curious to know how or if its possible that fuzzy logic can handle multiple inputs? Like multivariate data

Hi again! :-))

Well, I'm very happy and excited to see that my little project is getting some attention after I said "good enough for me", two years ago and wrapped it up without ever putting it up on a website, blog or some other kind public display or advertisement. So, if you don't mind me asking, how did you find out about it? What peeked your curiousity?
Also, why is it that when I get notified about your posts here, those emails apparently were sent from "Maximilian Schambach" via github?

Now I'll try to answer your questions as best as I can.

First, I need to emphasize something about fuzzy logic that I think got a bit lost. The whole point of mapping things to the unit interval [0, 1] is that, once you managed to do that, you can apply a broad range of well-researched mathematical tools to work with those values. Truth-values don't care how you produced them, and combining any number of them always will give you a new truth-value. This is also the reason why I often use fuzzylogic.functions as a standalone in my other projects - values in the unit-interval are just so fun and nice to work with once you got the hang of it.

Would whole building power be the domain for my application?

Well, to answer this you need to ask whether this information is relevant in making any decisions. Coming from your explanations, I would say "no", but I could imagine a scenario where subsystems like backup-battery or even a sprinkler (for passive water-cooling maybe?) could be switched on and off depending on your total power consumption. Or maybe if you want to send some alarms via telegram if consumption gets too high? You might say "why would I want to use fuzzy numbers for this, just use the total" but if you implemented everything with fuzzy numbers, you could recycle your code easily across many different systems because the mapping to unit interval basically functions as a common API :-)

The middle layer between fuzzification and defuzzication be all my IF ELSE statements about air flow and zone temp deviations?

Yes, that part would be combination and inference/rules. I tried multiple times to implement a proper fuzzy inference (IF/ELSE/THEN), but I never found an appealing way without completely messing up python syntax (ever tried to bastardize the Python if-statement with floats? Won't work.) or resorting to eval(), so I gave up and figured that plain and simple rules will do the job, and I still think it's true for most cases.

Curious to know how or if its possible that fuzzy logic can handle multiple inputs? Like multivariate data

Of course. You just need to be careful about the Domains and Sets. If you have many Sensors of the same model with the same considerations, you can probably reuse the same Domain with its Sets.

I fiddled a little with your stuff in a jupyter notebook and came up with this:

from fuzzylogic.classes import Domain
from fuzzylogic.functions import R, S, bounded_sigmoid

Air = Domain("Air_Flow", 0, 2000)
Dev = Domain("Stp_Deviation", -5, 5, res=0.01)
Air.still = bounded_sigmoid(500,1500, inverse=True)
Dev.too_low = S(-5,0)
Dev.too_high = R(0,5)

Here I tried to model some of the info you gave me that may warrant some kind of action.
You might be tempted to say action = Dev.too_low & Air.still or something like that, which is valid code, but it's basically undefined behaviour since it results in a Set with no domain (which makes sense, since the domain would be 2-dimensional now - or n-dimensional for an arbitrary combination of Domains). You still could pass in values, but I would strongly advice against combining sets across Domains like this (maybe there is some application for this I haven't thought of yet, though).
I've seen a couple different approaches to this (as with practically anything in fuzzy logic there is more than one "correct" way to realize something, as you might have noticed). If I recall correctly, a common one is MinMax (basically AND all sets of concern within a domain, OR across domains), so maybe action = lambda x, y: max(Dev.too_low(x), Air.still(y)) might do the trick.

Btw, IIRC I had some Rule class in fuzzylogic which would do something like this for you, but I think I took it out because I never found a good use for it. Maybe if you give me some example of possible actions you want triggered for some specific (multivariate) condition, I might come up with something nice for you.

Hi Amogorkon,

I ran across you repo just googling Python and fuzzy logic trying to learn more about it :) The Maximilian Schambach response seems real odd. I checked out his git repo seems pretty cool I wish I were as smart as that guy!

Could I ever show you like a Git Gist of some code I cobbled together from sci kit fuzzy example of the Tipping problem? I changed all of the wording around for my HVAC application, the algorithm outputs a value from 0-100, but there is a lot I dont understand. Curious if its easy/better to recreate with your repo if you could give me some tips? :)

Hey,

thanks for posting that link. I very much like how they realized the

rule1 = ctrl.Rule(quality['poor'] | service['poor'], tip['low'])
rule2 = ctrl.Rule(service['average'], tip['medium'])
rule3 = ctrl.Rule(service['good'] | quality['good'], tip['high'])

rule1.view()

That surely might be an idea for fuzzylogic, too - maybe for you to try and implement? I guess it's practically the same architecture as Domains and Sets, so it probably is mostly copy&paste and switching names.

Could I ever show you like a Git Gist of some code I cobbled together from sci kit fuzzy example of the Tipping problem? I changed all of the wording around for my HVAC application, the algorithm outputs a value from 0-100, but there is a lot I dont understand. Curious if its easy/better to recreate with your repo if you could give me some tips? :)

You even have to ask? ;-)
Of course I'll help out however I can with advice and quick code-cobbling. Just don't expect me to go for a deep code-dive for a while, got my plate full with other stuff right now.

Ok here's the Gist using scikit fuzzy example of modifying the tipping problem. Non computer science guy type of coding here :)

This is what I was sort of imagining. To simulate a building on a 24 hour time frame where typically dusk to dawn HVAC systems turn on, mechanical cooling operates, people in the building need ventilation, lights, outlet plugs, etc.. which makes the power consumption go up. The bottom FuzzyOutput % is what I was imaging the PID to do ramp up as power consumption gets over a setpoint. And when the ramp happens this is when we would do things to limit power consumption to the building automation system.

image

I have been just experimenting with trial and error. I've noticed in the membership functions changing this value .automf(7) from a 3,5, or 7 it gives me a few more options to choose from.

# Auto-membership function population is possible with .automf(3, 5, or 7)
building_power_error.automf(7)

Its sort of goofy basing decisions off of

dismal
poor
mediocre
average
decent
good
excellent

but it seems to be doing something :)

Curious if you have any thoughts? On the rule definition through trial error watching the fuzzy output plot I seemed to get best results NOT using a fuzzy_output['medium'])

EDIT, I changed some code to add a little noise to the plot lines.

image

Well, I've been fiddling around with fuzzylogic (or rather its predecessors) much longer than 8 years, which seems to be the time when they started work on scikit fuzzy.. which probably explains why I've never noticed it before you mentioned it. Before I built my own lib, I did some research and the things I found were mostly graphical tools for engineers and end-users written in Java with the intention of hiding anything that might look like code, which still seems silly to me.

To be quite honest, I don't really get what your "dismal, poor, mediocre, .." list actually represents in this case - I'm guessing fuzzy sets? You really need to be careful with your naming - for instance you wouldn't say "the temperature is poor today" but rather "it's freezing today!", also I advice to use as few sets as possible and instead put some effort into modelling them properly. For instance, I often prefer sigmoid/exponential functions or variations thereof instead of linear ramps because nature often is nonlinear. You probably only need two sets to describe your optimal temperature range - too cold and too hot. Anything "not (too_cold OR too_hot)" would be ideal. too_cold would be raising very fast below 21°C for me, so we're looking at a bounded_exponential or maybe a sigmoid function variant that drops more or less sharply from 1 to 0 close to 21. too_hot would be the same, just mirrored. Hmm. Looking at my bounded_exponential function, I'd say it that might need some tweaking for this exercise.

I also realized that my the fuzzylogic Domains could use physical units as optional keywords for proper plotting (maybe even unit conversion, but that might need external dependencies, which I'd rather avoid if possible).

For your exercise, you might also want to take actual time of day into consideration, since you said "near noon" and "near midnight" at some point. I remember I fiddled with a puzzle like that a while ago, was quite fun to solve.

On a sidenote, I'd much prefer my Domain.set notation over the dictionary["set"] notation in scikit fuzzy. It's not just less typing and looks nicer but my main gripe with dictionary notation is that IDEs won't help with auto-completion and typos will crash the program at some point while running due to a Key Error while bad dot.notation will immediately crash the program on startup due to a Syntax or Attribute error, making it much easier to debug.

Hi, I dont get what "dismal, poor, mediocre, .." means either it seems like the only choices with sci kit fuzzy... Or at least I could not figure out how to modify these names.

Would you have any examples that I could look at where I could use your library for this application? Use actual meaningful names that you mention as well as non linear relationship. All good stuff here including time-of-day that you mention would be extremely valuable. Thanks for suggesting.

Any tips to keep me going super gratefully appreciated. I think sci kit fuzzy package was just a beta version of an idea for something cool.

If you dont mind I may close this issue, study your repo a bit more, and post a new question :)

If you dont mind I may close this issue, study your repo a bit more, and post a new question :)

Well, our conversation never really was an "issue" anyway, so I'm fine with that ;)
I also thank you again for approaching me about this in such detail. Feeling motivated again to put some work into this lib at some point - never thought of simulations before.

Would you have any examples that I could look at where I could use your library for this application? Use actual meaningful names that you mention as well as non linear relationship. All good stuff here including time-of-day that you mention would be extremely valuable. Thanks for suggesting.

I'm not sure how valuable simulations actually are in your case, except for educational purposes. I think I would prefer looking at the plots of (combinations of) variables see how they behave over the whole defined range and then tweak those until they do what "feels right" - never understimate the gut feeling of an expert in their field.

Quickly cobbled together:

from fuzzylogic.classes import Domain, Set
from fuzzylogic.functions import R, S, triangular

hour_of_day = Domain("hour of day", 0, 24)
hour_of_day.near_midnight = Set(R(22,24)) | Set(S(0,2))
hour_of_day.near_noon = triangular(10,14)

hour_of_day.near_midnight.plot()
hour_of_day.near_noon.plot()

You could use

from datetime import datetime
datetime.now().hour

as input. If you don't like plain triangles, you could use triangular sigmoid or hedges like very to modify how steep the slopes are.

Cool! thank you ill play around with some code here shortly :)

I think I finally made the connection about your lib where the functions don't need to be triangular relationship. Like you mentioned if my data is non linear I could use any of these shapes. I think the classic non linear relationship for me in the industry I am in is electricity consumption and temperature, definitely non linear as well as HVAC operations.

image

With my background in pharmaceutical engineering I'm no expert in HVAC but I'd guess there are many more situations where system control requires nonlinear responses. I'd say whenever you're dealing with a large enough system that has some kind of inertia, you're right in nonlinear-land. Thinking of heat exchange.. large masses of moving fluids like water and air..

I think I finally made the connection about your lib where the functions don't need to be triangular relationship.

Happy to hear!

Just a sidenote on how the fuzzylogic.functions can be useful in other ways. Just today I wrote some code that would weigh some uncertain (and potentially unlimited) amount of "time spent" and "last time checked" against each other. For that, I just applied two fuzzy functions, guessing some values that should be "about right" and plugged them together in a fuzzy-logic way (without the rest of my lib, just the functions). I used some sigmoid functions that have no upper bounds and "just work" most of the time - no need to worry about overflows or the like, plain and simple. That's also a reason why I had decided to keep sets and functions seperate - it's so much easier that way to just use the functions when you don't need the logic wrapper of the sets.

Wow cool pharmaceutical engineering. Curious about how control algorithms are used in that industry... Ok so I have a real dataset historical one of a school with a chiller cooling system. Just creating some plots, here is a years worth of outside air temperature deg F., calculated tons of cooling load on the air cooled chiller (filtered for 0 tons when cooling system isnt running like winter time), and electricity consumption in kW.

image

Could I ever use this information to determine what "Shapes" I should use when defining my fuzzy logic rules? Sorry still researching fuzzy logic theory apologize if I don't have the correct words for the process. For example when you mentioned previously if

I don't want to use plain triangles, you could use triangular sigmoid or hedges

Could I ever determine this with a pairs plot to see the relationship of the data between 'OAT','Chiller_Load','Demand' For example linear or non-linear relationships... Would someone use triangle shape for linear relationships and like some sort of sigmoid shape for non-linear data relationships? Any tips/advice greatly appreciated! :)

The demand and outside air temp is a classic shape for people in this industry where its easy to see as weather outside getting warmer there is definitely increase in building power consumption for typical weather dependent cooling load. Its not like a hospital or industrial where cooling loads are dependent on other factors besides weather.

print(df2.corr()['Demand'].sort_values())
correlation

OAT             0.228906
Chiller_Load    0.257183
Demand          1.000000

image

Wow cool pharmaceutical engineering. Curious about how control algorithms are used in that industry

Most of what I've seen runs on Win95 or Win2000 machines with Java code you mustn't irritate by scratching your behind, or else it will stop the 40 year old pill machine that basically has two switches and 90% of the logic hardwired.. And then there are firms investing in quantum computing for research.
I'd say fuzzy logic in form of code like we're discussing here most likely can be found on the facility automation level, where things are a bit more relaxed in terms of validation requirements, like orchestrating different machines and production lines.

Could I ever use this information to determine what "Shapes" I should use when defining my fuzzy logic rules? Sorry still researching fuzzy logic theory apologize if I don't have the correct words for the process.

Very good questions! I think your approach is quite promising, although I've never tried it like this.
Maybe you can actually use this historical weather data etc. as input to what you think should be your fuzzy functions and then estimate how your fuzzy system would match or deviate from the actual measured power consumption.
You might want to try that for different extreme weather scenarios (very cold, very hot, very windy..) to test your assumptions and get a firm understanding of how the different shapes and variants of your membership functions (fuzzylogic.functions with hedges and combinators) affect the outcome.

I'm pondering whether or not this approach will require some kind of (simple) simulation, but on this level of abstraction you might not even need that.

Interesting cool stuff.. Sounds like the building automation industry ancient operating systems out there :)

Hi Anselm,

Ok so I am playing around with some code from the showcase.ipnb in your repo.

Am I at all on the right track for HVAC application?

import matplotlib.pyplot as plt
from fuzzylogic.classes import Domain
from fuzzylogic.functions import sigmoid
from fuzzylogic.rules import weighted_sum



level = Domain("fuzzy_output_%", 0, 10, res=1)
level.norm = sigmoid(1, 0.8, 10)
weights = {"outside_dry_bulb_temp": 0.2,
            "clg_load": 0.05,
            "outside_humidity":0.05,
            "setpoint_deviation": 0.7}

level.norm.plot()
plt.show()

w_func = weighted_sum(weights=weights, target_d=level)

# we've rated beverage etc. on a scale from 1 to 10 separately
levels = {"outside_dry_bulb_temp": level.min(9),
            "clg_load": level.min(5),
            "outside_humidity": level.min(4),
            "setpoint_deviation": level.min(8)}

print(w_func(levels))

This creates like a plot shown below.

image

I would need to create separate Domain for each feature related too power consumption right? Domain for the following:

  • outside_dry_bulb_temp
  • clg_load
  • outside_humidity
  • setpoint_deviation

And then weights for each of the domain listed above? Curious on how I could incorporate actual/real ranges that the data would fall under for each Domain

  • outside_dry_bulb_temp (0-100 degF)
  • clg_load (0-50 tons)
  • outside_humidity (0-100%)
  • setpoint_deviation (0-50 kW)

Hopefully that makes sense. Any chance for a tip on how I could butter up the code? :)

Hey Ben!

Hah! I didn't see that coming :D I'm not sure if a weighted sum will get you far on your quest, but it's an interesting thought. Your domains sound about right (what is a dry bulb btw?) , but what about the variables you're controlling?
In digital signal processing, a common way to analyse a complex system is to look at its impulse response - input a very strong and very short impulse and see how the system responds. Now, I was thinking how an impulse response would look like in your case - what would happen if you turned everything on to full power and then turn it off? Which variables would change, over which timeframe? Does heat transport depend on humidity? Also, I'm guessing since we're talking about conditions for humans to feel comfortable in, we also must take humidity into account for swetting. Could you open up all windows and just let the wind from the outside blow through the building to replace damp air?

I'm thinking of variables like

  • % windows opened
  • time of day (insects and stuff)
  • season (pollen, insects)
  • level of lighting (humans need ~1000 lux to really wake up and be productive)
  • sunrise/sunset
  • weather (cloudy/rainy/storm)
  • chill factor (taking into account relative humidity, temperature and airflow)

Maybe even put in some way for users to "vote" whether it should be hotter/colder etc. so that the system can automatically adjust output over time - there may be sensor drift or clogged up filters that don't get registered, so this could be a good way to notice trends and take preemptive action.

Hi,

Great stuff here... I need to study signal processing more... but I have been running some building energy simulations that can provide an hourly output to see how much the HVAC system power consumption should be. SO the variables to control would be related to taking the load off of the "chiller" which typical buildings is the largest power consumer during summer hot months.

The buildings I am dealing with have no operable windows, sort of lame the ones that there is no way to get that data into the building automation system.

For my energy analysis' that I have done with regression models these are the best independent variables. Electricity consumption is hard as there is a lot factors involved with it.

  • dry bulb outside air temp
  • wet bulb outside air temp (optional)
  • humidity (optional)
  • enthalpy (optional)

Somewhere in all of this I would have to factor in the occupant load as well in addition to weather that impacts power consumption. I don't have HVAC design experience, as my career has been all on the automation side but I know the designers factor in the number of people as people give off some heat that needs to be accounted for when sizing equipment. They also factor in lights as lights give off heat and also when a building is occupied there is a term called 'plug loads' which is the miscellaneous power consumption from outlets/computers, etc. SO something very generic like an occupancy schedule can account for lights/plugs as well as HVAC system operating.

Sorry on a rant here but also the occupancy schedule also accounts for "ventilating" the building. Where I live its very humid in the summer time so HVAC designers size systems to (1) remove the amount heat from the air (dry air or dry bulb air or sensible cooling load) and (2) factor in additional mechanical cooling sizing to remove the amount of moisture in the air which they have charts that account for blending % humidity together with dry air. Called wet bulb temperature, enthalpy, etc.. An air conditioning system for a wet humid climate maybe sized differently than a desert climate...

But usually when doing regression on energy consumption humidity is only a factor in summer months if the climate is a humid one. If you have ever heard of how Google started using AI in 2014 to improve data center efficiency, its these same ideas how they came up with the list to train the NN. Even a data center is very dependent on weather conditions. The list below is a copied from that white paper from the link. I found it interesting they factored in Wind Speed as well I personally haven't seen a much an effect Wind has on a building energy use when using regression analysis but it totally depends on the building shell (is "leaky" or not). SO generally its a list of operating mechanical equipment (I was trying to calculate mechanical cooling load) sensor readings/speeds its running at, weather data, and occupancy load. (data centers are 24/7 so they didnt include occupancy load)

Copy pasted from the Google white paper
The neural network features are listed as follows:

  1. Total server IT load [kW]
  2. Total Campus Core Network Room (CCNR) IT load [kW]
  3. Total number of process water pumps (PWP) running
  4. Mean PWP variable frequency drive (VFD) speed [%]
  5. Total number of condenser water pumps (CWP) running
  6. Mean CWP variable frequency drive (VFD) speed [%]
  7. Total number of cooling towers running
  8. Mean cooling tower leaving water temperature (LWT) setpoint [F]
  9. Total number of chillers running
  10. Total number of drycoolers running
  11. Total number of chilled water injection pumps running
  12. Mean chilled water injection pump setpoint temperature [F]
  13. Mean heat exchanger approach temperature [F]
  14. Outside air wet bulb (WB) temperature [F]
  15. Outside air dry bulb (DB) temperature [F]
  16. Outside air enthalpy [kJ/kg]
  17. Outside air relative humidity (RH) [%]
  18. Outdoor wind speed [mph]
  19. Outdoor wind direction [deg]

*There really isnt too much of a difference for what Google was doing to improve datacenter efficiency that I am hoping to do :)

What they dont talk about is exactly what they are doing that is the real mystery. My only ideas that if the PID or Fuzzy logic output a value from 0-100% I could start turning the air conditioning off in zones which would lower power consumption. Exactly how much it would lower power consumption per zone its not exactly known as the dynamics of the system is constantly changing but i have an idea on how to prioritize rank zones.

Alot of info here thanks for any time you have in response! OR help out with the Domains Idea!

The author of the Google white paper does a real cool job at describing how the non-linear loads of HVAC system...

I have been thinking about this a bit a little bit more and your most likely correct, but the logic I was thinking about is regression analysis with weather and energy data. At least you can see which independent weather variables has most impact on energy consumption. Maybe that wouldn't be apples to apples or applicable :)

I'm not sure if a weighted sum will get you far on your quest

Anyways no rush on this experiment but any tips on creating some code greatly appreciated :)

Hey Ben,

sorry for letting you wait on my reply. I've been pretty busy with Watnu and writing a report while moving to VSCode from Sublime Text. I read your comments, but I'm a bit at a loss as to how I can help you with your quest. Maybe I can build you a Rule class that lets you handle your regression stuff easier?

Sure! Also, do you have anything you came up with that I should include in the lib? Or anything holding you back to be productive with the lib I should fix/change?

Only thing for me is I know very little about fuzzy logic theory and some examples would be cool to add to the lib :)

So this gist when I run prints this plot at the end. The fuzzy logic output (purple) is what I was trying to dial in with simulating.

image

The code I cobbled together from this example.

Thru trial and error on the simulating this script over and over, I found I could get the fuzzy logic output (0-100%) to react in a way that I would want to see it in the real world with tuning these values highlighted yellow bellow while keeping outside air temp & building electricity more linear.

image

I think it could be something to do with a non-linear relationship of how it all works :) But just simulating over and over it seemed to work more and more how I would expect it. Odd thing is (very well known concept) is electricity consumption in buildings is always a non-linear relationship to outside air temperature...

Its been a fun concept, hoping to try it out this summer on a live system :)

Hey Ben,

Now that I've finished a pre-alpha of my other project for testing, I'm happy to have a stab at fuzzylogic again.
Looking at your examples, I think I'm having a fairly good idea what you need and will tackle implementing a Rule class shortly.

I'm just having a closer look at fuzzy control rules. Just found this paper, what do you think?

I was just tinkering on a Rules class, what do you think of this syntax?

import classes, functions
from hedges import very

temp = classes.Domain("Temperature", -80, 80)
hum = classes.Domain("Humidity", 0, 100)
motor = classes.Domain("Speed", 0, 2000)

temp.hot = functions.R(30,40)
temp.cold = functions.S(0,15)

hum.dry = functions.S(20,40)
hum.wet = functions.R(40,70)

motor.fast = functions.R(1000,1500)
motor.slow = ~motor.fast

R1 = classes.Rule({(temp.hot, hum.dry): motor.fast})
R2 = classes.Rule({(temp.cold, hum.dry): very(motor.slow)})
R3 = classes.Rule({(temp.hot, hum.wet): very(motor.fast)})
R4 = classes.Rule({(temp.cold, hum.wet): motor.slow})

Rules = classes.Rule({
                      (temp.hot, hum.dry): motor.fast,
                      (temp.cold, hum.dry): very(motor.slow),
                      (temp.hot, hum.wet): very(motor.fast),
                      (temp.cold, hum.wet): motor.slow
             })

Rules == R1 | R2 | R3 | R4 == sum([R1, R2, R3, R4])

print(Rules(temp(5), hum(50)))

I was thinking that the Rule class should - similarly as the Set class is wrapping functions - wrap a dict with additional logic and abstractions. With that, you first have the way of noting every rule by itself, just like you did in your snippet a while ago. Considering that the only thing left to do is pass in the values from the domains to map and compute accordingly, I didn't like the idea of writing yet another class just to collect the rules and pass in values, so I wondered about concatenating rules and passing in values directly.
Now, with this notation, you can write a single big block of rules, which will help if you have a lot of domains and sets and you want to exhaust all possible combinations without losing the overview. Or you can write simple rules all over the place and cobble them together with | or + on the spot, or you can collect all rules in a single iterable and just use sum(...), which should simplify code of bigger systems.

The tuples in the snippet are a bit misleading. Since position shouldn't matter, all iterables are turned into frozensets when first passed into a rule, which should enable a nice and simple syntax while avoiding common problems. For the Domain(value) syntax to work in conjunction with Rule, I had to change the output of Domain.__call__, but that shouldn't be a big issue.

Okay, after venturing deeper into fuzzy rule land, I found a few peculiar things.

for r, v in Rules(temp(0), hum(40)):
    print(r.domain, r.name, v)

Speed very_slow 0.19999999999999996
Speed slow 0.14285714285714285

I've changed the behaviour of hedges. Now those "anonymous" functions will adopt the name of their predecessor with "very_" etc. and its domain, so they can be debugged much easier, however, they are not assigned to the domain automatically, so no unexpected behaviour there. When evaluating the rules, now everything with 0 membership is ignored - which opens the possibility of a very strange kind of bug: what if all rules become 0?
That specific case can happen if membership functions are defined without overlaps. Say,

temp.cold = functions.S(0,15)  # here is a gap between 15 and 30
temp.hot = functions.R(30,40)

hum.dry = functions.S(20,35)  # here is a gap at exactly 35
hum.wet = functions.R(35,70)

which can cause undefined behaviour with values exactly within those gaps.

This got me thinking - is it desirable to have rules that consider only one or few domains?
Like, for instance, you could define a general function that is defined over the whole range as a kind of fall back if one really wants to work with gaps..
That would allow the user to define some general behaviour and let them gradually add specific cases..
On the other hand, that should also be doable already - just do

temp.general = functions.R(temp._low, temp._high)
motor.failsafe = functions.R(motor._low, motor._high)

failsafe_rule_temp = classes.Rule({ 
                                       (temp.general, ) : motor.failsafe
                                       })
weights = Rules(temp(16), hum(35))
if not weights:
    weights = failsafe_rule_temp(temp(16)
else:
   ...

Or I could print/log/raise a warning like "warning: undefined behaviour" or somesuch..?

Edit: I guess returning None will work. Then one can plot the whole thing and check for NaN that way.

Given overlapping sets, it actually should work now as expected.

import classes, functions
from hedges import very

temp = classes.Domain("Temperature", -80, 80)
hum = classes.Domain("Humidity", 0, 100)
motor = classes.Domain("Speed", 0, 2000)

temp.cold = functions.S(0,20)
temp.hot = functions.R(15,30)

hum.dry = functions.S(20,50)
hum.wet = functions.R(40,70)

motor.fast = functions.R(1000,1500)
motor.slow = ~motor.fast

R1 = classes.Rule({(temp.hot, hum.dry): motor.fast})
R2 = classes.Rule({(temp.cold, hum.dry): very(motor.slow)})
R3 = classes.Rule({(temp.hot, hum.wet): very(motor.fast)})
R4 = classes.Rule({(temp.cold, hum.wet): motor.slow})

Rules = classes.Rule({(temp.hot, hum.dry): motor.fast,
                      (temp.cold, hum.dry): very(motor.slow),
                      (temp.hot, hum.wet): very(motor.fast),
                      (temp.cold, hum.wet): motor.slow,
                      })

Rules == R1 | R2 | R3 | R4 == sum([R1, R2, R3, R4])

print(Rules(hum(30), temp(9)))

will now return 589.033 - the actual, expected, crisp value in the domain of motor. I was testing the results by plotting things, but I gave up on plotting 2D surfaces (which is more a problem with matplotlib than with fuzzylogic :p ), but from what I could see, it's behaving exactly as expected.

One thing I'm a bit struggling still is the asymmetry with the left side of the rules behaving a little different than the right because you can't just plug in hedges there, unlike the right side.

I think your example could be realized now basically with

rules = Rule({
         (outdoor_temp.extreme_low, building_power_error.extreme_low, chiller_load.medium_low): fuzzy_output.no_adj,
         (outdoor_temp.low, building_power_error.low, chiller_load.medium): fuzzy_output.no_adj,
         (outdoor_temp.medium_low, building_power_error.medium_low, chiller_load.medium): fuzzy_output.no_adj,
         (outdoor_temp.medium, building_power_error.medium, chiller_load.medium): fuzzy_output.no_adj,
         (outdoor_temp.medium_high, building_power_error.medium_high, chiller_load.medium): fuzzy_output.top_level_adj,
         (outdoor_temp.high, building_power_error.high, chiller_load.medium): fuzzy_output.top_level_adj,
         (outdoor_temp.extreme_high, building_power_error.extreme_high, chiller_load.medium_high): fuzzy_output.top_level_adj
})
fuzzy_output_ctrl = rules(outdoor_temp(t), building_power_error(e), chiller_load(c))

I think there are a few advantages here: with sets as real attributes of domains, it is not just nicer to read and less to type, but most importantly IDEs can easily help with autocompletion, type checking and preventing typos. With Set and Domain classes now working hand in hand the Rule class can be as simple and lightweight as possible.

With Domain, Set and Rule classes now fully integrated, the system now should be complete. I don't expect huge issues with the way things are implemented and performance-wise I also don't foresee any issues for real-world applications intented for controlling stuff.

I think I'll change the way Rule is called, that should fix the symmetry issue and possibly allow native numpy handling of arrays, which should speed up plotting and possible simulations over the whole space, spanning all domains.

Now Rule is called like

rules = classes.Rule({(temp.hot, hum.dry): motor.fast,
                      (very(temp.cold), hum.dry): very(motor.slow),
                      (very(temp.hot), hum.wet): very(motor.fast),
                      (plus(very(temp.cold)), hum.wet): motor.slow,
                      })

print(rules({hum:50, temp:0}))

which fixes the symmetry - allowing hedges and combinators on both sides of the condition :-)
I'll have to tinker a little bit more if you really want to pass in arrays, but it should be fairly straight forward from this point.

Hi @amogorkon !

Code looks cool. It would be really cool being able to simulate fan motor example. Would you have any ideas or sim packages to try? I was curious about this package.

Or have you ever heard of anyone use open Modiclica before?

Or doings something like this would be super cool

Is your repo update on Pypi? If I started playing around with this code you mentioned previously

import classes, functions
from hedges import very

temp = classes.Domain("Temperature", -80, 80)
hum = classes.Domain("Humidity", 0, 100)
motor = classes.Domain("Speed", 0, 2000)

temp.cold = functions.S(0,20)
temp.hot = functions.R(15,30)

hum.dry = functions.S(20,50)
hum.wet = functions.R(40,70)

motor.fast = functions.R(1000,1500)
motor.slow = ~motor.fast

R1 = classes.Rule({(temp.hot, hum.dry): motor.fast})
R2 = classes.Rule({(temp.cold, hum.dry): very(motor.slow)})
R3 = classes.Rule({(temp.hot, hum.wet): very(motor.fast)})
R4 = classes.Rule({(temp.cold, hum.wet): motor.slow})

Rules = classes.Rule({(temp.hot, hum.dry): motor.fast,
                      (temp.cold, hum.dry): very(motor.slow),
                      (temp.hot, hum.wet): very(motor.fast),
                      (temp.cold, hum.wet): motor.slow,
                      })

Rules == R1 | R2 | R3 | R4 == sum([R1, R2, R3, R4])

print(Rules(hum(30), temp(9)))

I don't know any of the simulation packages, sorry, but I'm pretty sure you can do those simulations now with fuzzylogic, since it is really just about pluging in numbers and passing output as input..
What I still would like to do at some point is to "overload" all the functions, so that numpy arrays are handled just like single floats to allow native vectorization with no extra abstraction, but you don't need that for simulations, really.

Hey Ben,
I updated pypi a few days ago, but I'm not sure if it has the latest changes.

I'd still like to be able to pass in numpy arrays into the functions for native vectorization, but that actually shouldn't be of interest to you, since simulations etc. already should be doable fairly easily, although I have no experience with any of the packages you linked.

Please let me know if there is any crucial feature you need in fuzzylogic, I'll try to fit it in.

I've worked with twisted a couple years ago, which was basically the blueprint for asyncio, and I've experimented a little with coroutines and async stuff when it came out, but I've never done any serious work with asyncio itself.

The other day I had that thought that, similarly how AI pushed Python to become a "lingua franca" in general, it could be that the Internet of Things actually could push fuzzy logic to become a "lingua franca" with all the cheap sensors whose output need to be merged together in a meaningful way to control equally many cheap actors.. what do you think?