overthesun / simoc

A scalable model of an interactive, off-world community
https://ngs.simoc.space/
GNU General Public License v3.0
2 stars 2 forks source link

Implement "elemental validation" #128

Open ezio-melotti opened 3 years ago

ezio-melotti commented 3 years ago

This issue lists possible changes/improvements to the ABM, that will solve some of the issues we had in the past and make SIMOC more flexible:

Agents and Storages

Currently we have agents that look like black boxes that get some inputs and produce some outputs at every step:

          +-------------+
          |/////////////|
---O2---->|/////////////|>--CO2----
---H2O--->|////HUMAN////|>--sweat--
---food-->|/////////////|>--urine--
          |/////////////|>--feces--
          |/////////////|
          +-------------+

Then we have storages that are boxes that contain some currency:

          +-------------+          
          | AIR STORAGE |
          |+-----------+|
>---O2----|| O2 STOR   ||---O2---->
          |+-----------+|
          |+-----------+|
>---CO2---|| CO2 STOR  ||---CO2--->
          |+-----------+|
          |+-----------+|
>---...---|| ... STOR  ||---...--->
          |+-----------+|
          +-------------+

Even though they both have inputs and outputs, there are currently 3 major differences between agents and storages:

  1. Storages contain currencies, agents do not;
  2. Agents are active, storages are passive;
  3. Agents perform transformations, storages do not.

Active means that they seek and pull inputs and push out outputs; passive that inputs and output are pushed in and pulled out by other agents. Performing transformations means that the inputs are processed to produce different outputs.

Note: in the diagram I'm using --xxx-->|agent| to indicate an active input (the agent pulls in the input), and >--xxx--|storage| to indicate a passive input (the input is pushed into the storage). Similarly |agent|>--xxx-- is an active output (pushed out), and |agent|--xxx--> is a passive output (pulled out by an agent).

Note: technically not all agents have both inputs and outputs: we also have sources (agents that only produce outputs "without" input, e.g. solar panels) and sinks (agents that only take inputs "without" output, e.g. the CO2 scrubber).

Agents as storages

The first change that I propose is to have the agents act as storages too:

          +-------------+
          |    HUMAN    |
          |+---+   +---+|
---O2---->||O2 |-->|CO2||>--CO2----
          |+---+   +---+|
          |        +---+|
          |     /->|swe||>--sweat--
          |+---+   +---+|
---H2O--->||H20|        |  
          |+---+   +---+|
          |     \->|uri||>--urine--
          |     /  +---+|
          |+---+   +---+|
---food-->||foo|-->|fec||>--feces--
          |+---+   +---+|
          +-------------+

After this change, agents and storages wil have 2 similarities and 2 differences: They will both:

But:

Note: the transformation doesn't need to be defined accurately at a chemical level -- it is enough that at each step a certain amount is removed by each of the input storages (the boxes inside the human on the left) and a certain amount is added to the output storages (the ones on the right). The elemental validation will take care of making sure the amounts match.

This said, I'm not sure what the best way to think about this would be:

All agents are storages (or vice versa)

Unifiying agents and storages (at least at some level) allows us to solve several problems:

Connections

Problems:

Proposed solution:

Grouping

Problem:

Proposed solutions:

Priorities

Problem:

Depending on the model, there are different kinds of priorities:

Since the exchanges happen between different agents, they might have different needs and priorities. For example a food storage might want to prioritize the use of biomass_edible, but an agent might want to get food_ration (this is solved if the grouping exposes both food and the individual food_ration and biomass_edible).

Proposed solution:

Conclusions

There are still several things that should be determined/clarified, and we should also verify if this model is compatible with the libraries that we are using (e.g. defining priorities, tracking internal storages, etc.). I think several -- if not all -- of these changes can be done incrementally, without having to rewrite everything from scratch.

Notes: ¹ Storages and their connections are hardcoded here: https://github.com/overthesun/simoc/blob/9131917125aba96d9d8e4b998cdf387b652e7ab2/simoc_server/front_end_routes.py#L183-L227 ² If all agents become storages, the connections will no longer be between agents and storages but just between agents

granawkins commented 3 years ago

Closed by mistake

granawkins commented 3 years ago

These additional items are from Kai's conversation with Ray Wheeler on 22-Sept:

(original:) a) plant start/harvest staggering^ b) invoke random variation (entropy engine)^ c) human routine vs energy consumption and sleep d) a correlation between light duration and plant growth

^Also mentioned in https://github.com/overthesun/simoc/issues/105 (Phase V meta-issue).

Plant Start/Harvest Staggering

I think this can be managed by the EnclosedAgent class.

Here is EnclosedAgent.step(): https://github.com/overthesun/simoc/blob/7dc4f780d4a87c6305629b5763de64303ddc068e/simoc_server/agent_model/agents/core.py#L178-L195

One way is to add a parameter stagger characteristic in agent_desc for each agent next to lifetime, and in EncloseAgent.step() do something like:

if self.stagger > 0:
    self.stagger -= time_delta
    // skip GeneralAgent.step()
if self.grown and not self.reproduce:
    self.destroy()
else:
     return // proceed to GeneralAgent.step()

Entropy Engine

From meeting minutes 09/22a, Ezio proposed 3 conceptual levels of entropy:

1) Entropy introduces random fluctuations in currency exchange 2) Mechanical system leaks: some leaks are lost to the exterior; some internal and accounted for 3) Total systems failure (e.g. airlock failure, plant demise, meteorite impact); game play

Ezio suggests that not all agents should be engaged at the same level of entropy, such that a lemon might be harvested as considerably smaller or larger, but the CO2 scrubber should not fluctuate at a much lower rate. So maybe we encode in each agent description an upper and lower boundary, or an additional, fixed variable that mitigates the entropy function. // .. // I think we should have:

  • per-agent/per-connection entropy boundaries
  • customizable default boundaries (e.g. ±5%)
  • a bool value to turn off the entropy engine

If the entropy engine is on, and an agent/connection has specific entropy boundaries, they will be used, if they don't, the default boundaries will be used. If the entropy engine is off, no changes will be applied.

The backend appears to support 'noise' for both daily_ and lifetime_growth. It's input as a boolean value in the agentdesc, and given a default value of 10 in all the `get[...]_curvefunctions ingrowth_func.py`.

First step (currently working on) is verifying that this existing function works, and considering if/how it plays into the above plan.

Human Input Daily Variation

As I understand this would reflect how

I think these can use daily_growth_functions, similar to solar. Solar uses the switch function, which is calculated as a normal dist. https://github.com/overthesun/simoc/blob/7dc4f780d4a87c6305629b5763de64303ddc068e/simoc_server/agent_model/agents/growth_func.py#L249-L283

We could add new functions, for example get_human_activity_curve or get_meal_curve, and use them in the relevant input/output fields.

Correlate Growth with Light

per Kai's comment in the original email:

the code is already there but working in reverse--if the light is reduced the plant suffers. We can change this to be a linear function of light and plant growth.

granawkins commented 3 years ago

Progress Update

Connections (complete)

PR #149 was a first step of the agent redesign which:

Agents as Storages (in progress)

High-level objectives are:

The rough plan is to combine GeneralAgent and StorageAgent into a single class. Some considerations that should be addressed before proceeding:

Instances vs Amounts

There are basically 2 ways to 'scale-up' an agent:

  1. StorageAgents use multiple instances of the same agent. In front_end_routes.convert_config(), if the starting balance for currency is higher than the StorageAgent's capacity, a new instance is created. GeneralAgents have a separate connection to each instance, and inputs/outputs are split evenly between instances. https://github.com/overthesun/simoc/blob/7dc4f780d4a87c6305629b5763de64303ddc068e/sample_game_config.json#L146-L155

  2. GeneralAgents use a single instance with an amount, and multiply inputs/output values by the current amount. This amount is incremented down by the deprive function. Agents could use multiple instances, but SINGLE_AGENT is always set to 1 (see frontend/src/store/modules/wizard line 249). https://github.com/overthesun/simoc/blob/7dc4f780d4a87c6305629b5763de64303ddc068e/simoc_server/agent_model/agent_model.py#L775-L793

It seems that StorageAgents could use the second approach (amounts) to achieve the same functionality, and therefore we could eliminate instances altogether. ...convert_config() would increase the amount of a storage rather than create a new instance, and 'capacity' would be multiplied by the amount. This reduces complexity, and means that connections.json doesn't specify an instance, but it makes it doesn't allow different instances to function independently (per the scuba_tank item above).

Duplicated code

In two areas particularly:

These could be specified at some 'higher level'. We already use 'classes' in the agent_desc: all the crew_habitat_[size] are part of structures, and plants are part of plants. We could add class attributes somewhere which are used, unless specified explicitly:

Alternatively we could add 'prototypes' with the attributes that apply to all, and add e.g. prototype: plant attribute to all the plants in the agent_desc.

Balance Atmospheres

Suppose humans connect to habitat and plants connect to greenhouse. If habitat/greenhouse are isolated, this works perfectly, and eclss agents (e.g. co2_removal_sawd can transfer gasses between the two. However if they're not isolated, we would need to either:

  1. Connect humans/plants to both, and split inputs/outputs between the spaces (should be proportional to volume).
  2. Combine the two into a single agent in convert_config, and set human/plant connections to the combined agent.
  3. Have a 'door' agent which equalizes the atmospheres of habitat/greenhouse every step.
ezio-melotti commented 3 years ago

This is a list of other things we discussed that are more or less related to the ABM. Some of these ideas are still half-baked and not well-defined yet, or are features we might want to add further down the line. They are no in no specific order (even though I tried to discuss related items close to each other).

Variable inputs/outputs

Right now the agents are either on/off, so e.g. the ECLSS runs at full capacity if the CO2 goes above a certain threshold and turns off completely if it falls back below the threshold. We might want to have a way to e.g. run at half capacity.

This came up while discussing the "door" agent that should exchange air from the greenhouse and the crew quarters: currently it could either be open or close, but not half open. A workaround for this is having it open and close at alternating steps.

Compound deprivation

We might want to trigger deprivation only if a combination of events occur. This came up while discussing pressure combined with O2 levels (even if apparently we won't need compound deprivation for this). Another example might be in a situation where both rations and harvested plants run out: the deprivation shouldn't trigger when only one of the two runs out (unless we combined as discussed in the first message, in the grouping section).

Related: should the deprivation be calculated on the internal storage instead/in addition to the inputs? E.g. if there is no water left, but plenty of watermelons, can the human survive by eating watermelons without drinking water and without dying of dehydration?

Mixed storages

Currently the air storage has separate "sub-storages" for O2, CO2, etc., but in reality these are mixed and share the same volume. Something similar might also apply to the food storage, where we might have a "shelf" with a certain volume, and we could fill it with different kinds of harvested plants that share the same space.

This is different from e.g. water storages, where each type of water has to be isolated by the others. How can we represent this distinction? How does pressure affects this in case of the air storage?

Habitat pressure

We should track habitat pressure.

Currency names

Currently we are using names like atmo_co2, h2o_potb, etc.. These are useful when you need a single label to identify unequivocally a certain currency in a certain context, but it makes it more difficult to do other things.

With all agents becoming storages, the currencies will either be inside an agent or inputs/output of an agent. We could use a naming scheme like agent_name.currency_name and perhaps agent_name.[in/out].currency_name for connections. For example, atmo_co2 will become crew_quarters.co2, and the humans will exhale human_agent.out.co2. T

Storage views

There are different ways to look at the content of a storage. For example, in the air storage we might have a certain amount of O2 and CO2. We might also represent this as a certain amount of carbon and oxygen atoms (the elemental validation might look at this representation). For the humans, we could keep track of how much H2O they drank and how much hydrogen and carbon there is in the human. We might also want to have a different view for macronutrients or calories.

Each view can provide a different breakdown of some aspect of the content of the storage.

Track calories and habitat temperature

We also want to track calories, and this applies to both food and machines (since they produce heat).

Entropy

Each agent in the agent_desc.json might have a different entropy level. This could apply to all values or just to some. For example some plants might grow twice as big as other plants of the same type, but solar panels might be only slightly more or less efficient that other similar panels.

The value could be expressed as a pair of values, indicating min and max variation, with 1 as "no variation". For example a pair like [0.8, 1.2] indicates a possible variation of ±20%, [0.95, 1] could indicate that up to 5% could be lost in a leak, but no more than 100% could be transferred.

Entropy (nature vs nurture aka initial vs variable aka fixed vs variable)

The entropy can be applied both as an initial multiplier when the agent is created, and for each exchange. For example we could have a really good seed that will produce a plant twice as big as the others, but each exchange will also be subject to some amount of entropy. The formula will look something like base_value * fixed_agent_entropy * variable_agent_entropy.

The seed could have an initial entropy between [0.1, 2.5] and 2 might be the random initial value, and then the inputs/output might be in range [0.8, 1.2]. If the plant normally draws 3g of CO2 for each step as a base value, this seed might draw 3g * 2 * 0.86 == 5.16g.

Connection exchange direction (pull vs push)

When to agents are connected, sometimes an agent produces a certain amount as output for each step and "pushes" it out, whereas other times it contains a certain amount (as storage), and part of it gets pulled. For example, a human exhales a certain amount of CO2 as output, and the air_storage can provide a certain amount of CO2 as output for the plants. In the first case, the human is pushing out the CO2, in the second the plants are pulling out the CO2 from the air_storage.

This might also affect how entropy work. For example a plant takes CO2, H2O, nutrients, and light as inputs. For the CO2 and nutrients, it could pull how much it wants (assuming there is enough), but the H2O and light are provided in fixed amounts. The entropy shouldn't be able to determine e.g. how much light the plant will "pull" (in theory they could pull less, but not more), but it can determine how much light the lamps will "push" (less or more). This issue might be solved by setting an entropy of e.g. [0.9,1.1] for lamps.out.light and an entropy of e.g. [0.95, 1] for plant.in.light.

Entropy (random events)

We could have a series of events that happen randomly, e.g.: sand storms, solar flares/storms, equipment failure, diseases, accidents, meteorite strikes, etc. Each of these event have a certain (very low) probability to happen at each step. Each even will also have a severity that will tell how serious the consequence of the event might be (as a range), and possible a duration/recovery time before the effects disappear (also a range).

Wellness index

We should figure out a "wellness index" for humans, that represents how well they feel based on the amount of food/water/sleep/work they had/did.

A similar concept can be applied to other agents as well, including machines. The baseline for the index could degrade as the agents age, and might also be subject to random fluctuations caused by external factors (e.g. humans not getting enough food/water/sleep, being overworked, etc.).

Random events and Wellness index interaction

If we take the Coronavirus as an example, there is a certain probability the event will happen and the human will get infected. This will be affected and will affect the wellness index based on the severity of the infection. For example, an underfed and overworked human might have a lower wellness index (they will be weak), so once they gets infected they might need a longer recovery time. During this time, the wellness index will be further lowered. Another human might be struck with a lower severity and recover much quickly.

For a sand storm, the severity might affect how many/how much the solar panels will be covered in sand, and the duration how many hours will pass before the sand will go away.

The way the event affect the agent might also be described as a function (e.g. e.g. the sand on the panels might decrease linearly, whereas an infection might start mild, get worse, then mild again before disappearing).

granawkins commented 3 years ago

From the meeting minutes 2021 10/17:

INCLUDE (go live by end-of-year)

  1. Different storages, same currency; atmo_co2 can be in the habitat, or in a storage tank
  2. Prioritized connections; if fresh food isn't available, use rations
  3. Nutrition; see the protein/carbs/fat breakdown that humans are eating at any given moment
  4. Entropy; include randomness, tie to model seed to maintain reproducibility a. Global entropy; (0-1) determines overall 'weirdness' of the sim, affects all below b. Agent Initial; some instances of agents produce/consume more than others c. Agent Step-wise: values fluctuate at each step. Currently implemented as noise. d. Agent Random event; e.g. some things have a set probability of occurring at any step e. Agent Decay; e.g. chance of humans getting sick increases reverse-exponentially with age
  5. Stagger plant start/harvest; e.g. after the tomatoes are harvested, plant squash
  6. Circadian rhythm; human inputs/outputs follow daily growth functions, e.g. eat 3x/day, respiration different while sleeping
  7. Wellness Index; a single value that defines the overall wellbeing of the humans. Incorporates e.g. decay, diet

TBD

LATER (include in a future redesign)