Skip to content

Commit

Permalink
Merge pull request #175 from pik-copan/group_entity
Browse files Browse the repository at this point in the history
Group entity
  • Loading branch information
jnnsbrr authored Jul 4, 2024
2 parents b3592d2 + 7c31ec4 commit d937258
Show file tree
Hide file tree
Showing 28 changed files with 745 additions and 18 deletions.
8 changes: 6 additions & 2 deletions CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ cff-version: 1.2.0
message: If you use this software, please cite it using the metadata from this file.
type: software
title: 'pycopancore: Reference implementation of the copan:CORE World-Earth modelling framework'
version: 0.6.0
date-released: '2020-04-28'
version: 0.7.0
date-released: '2024-07-04'
abstract: The pycopancore package is a python implementation of the copan:CORE
modeling framework as described in this paper. The framework is designed to
allow an easy implementation of World-Earth (i.e., global social-ecological)
Expand All @@ -14,6 +14,10 @@ abstract: The pycopancore package is a python implementation of the copan:CORE
stochastic and deterministic events and therefore allows to compare different
model and component types and implementations.
authors:
- family-names: Bechthold
given-names: Max
email: [email protected]
orcid: 0009-0007-7113-4814
- family-names: Heitzig
given-names: Jobst
email: [email protected]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ may not be sufficient and a distinction of social strata or other social groups
"indigenous people", "social democrats", a certain NGO, ...)
that is transverse to the former partitioning is helpful in addition.

For this, we will in the future provide an entity-type "group"
For this, the entity-type "group" is provided
which is meant to represent any grouping of individuals (that may come from one or several social systems)
by meaningful cultural or social-metabolic aspects.

Expand All @@ -33,16 +33,19 @@ Basic relationships to other entity-types

A group will usually...

- have several member :doc:`individuals<individual>`
- have several members :doc:`individuals<individual>`,
which is represented by a group membership directed network owned by the culture taxon

In addition, a group may...

- have one or several "leader" :doc:`individuals<individual>`,
- have an "intra" group network between members :doc:`individuals<individual>`

- have one or several "leader" :doc:`individuals<individual>`,
of which one may be the dominant leader

- have a "headquarters" :doc:`cell<cell>`

- be related to other groups via some network owned by the culture taxon
- be related to other groups via an "inter" network owned by the culture taxon
(which will typically interact with the network of personal acquaintance between member individuals)

- act as the current "elite" in some :doc:`social system<social system>`
Expand Down
3 changes: 3 additions & 0 deletions pycopancore/data_model/master_data_model/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,8 @@
from .individual import Individual as I
from .individual import Individual as individual
from .individual import Individual
from .group import Group as G
from .group import Group as group
from .group import Group

from .. import unity
19 changes: 18 additions & 1 deletion pycopancore/data_model/master_data_model/culture.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,24 @@ class Culture:
scale='nominal',
datatype=set)



# group entity related networks

inter_group_network = \
Variable("inter group network",
"""Basic undirected social network between
Groups.""",
ref="https://en.wikipedia.org/wiki/Social_network#Meso_level",
scale='nominal',
datatype=Graph)

group_membership_network = \
Variable("group membership network",
"""Directed network from individual to group that
signifies membership (to avoid problems due to n to n relation)""",
scale='nominal',
datatype=DiGraph)

# socio-cultural traits that may occur on different levels:

is_environmentally_friendly = \
Expand Down
31 changes: 31 additions & 0 deletions pycopancore/data_model/master_data_model/group.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""Master data model for group."""

from .. import Variable

from networkx import Graph

class Group:

#TODO: specify edges

intra_group_network = \
Variable("intra group network",
"""Basic undirected social network between
Group members.""",
scale='nominal',
datatype=Graph)

has_leader = \
Variable("has a leader",
"whether the group has a leader",
scale="ordinal", levels=[False, True], default=False)

has_headquarter = \
Variable("has a headquarter",
"whether the group has a headquarter located in a cell",
scale="ordinal", levels=[False, True], default=False)





1 change: 1 addition & 0 deletions pycopancore/model_components/abstract/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from .social_system import SocialSystem
from .cell import Cell
from .individual import Individual
from .group import Group

from .environment import Environment
from .metabolism import Metabolism
Expand Down
25 changes: 25 additions & 0 deletions pycopancore/model_components/abstract/group.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""Abstract Group entity type class, inherited by base model component."""

# This file is part of pycopancore.
#
# Copyright (C) 2016-2017 by COPAN team at Potsdam Institute for Climate
# Impact Research
#
# URL: <http://www.pik-potsdam.de/copan/software>
# Contact: [email protected]
# License: BSD 2-clause license

from ...private import _AbstractEntityMixin
from ...data_model import OrderedSet


class Group (_AbstractEntityMixin):
"""Abstract Group entity type class.
Inherited by base model component.
"""

variables = OrderedSet()
"""All variables occurring in this entity type"""


Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from .social_system import SocialSystem
from .cell import Cell
from .individual import Individual
from .group import Group

from .environment import Environment
from .metabolism import Metabolism
Expand Down
19 changes: 17 additions & 2 deletions pycopancore/model_components/base/implementation/culture.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@

from .. import interface as I

from networkx import Graph

from networkx import Graph, DiGraph

class Culture (I.Culture, abstract.Culture):
"""Culture process taxon mixin implementation class."""
Expand All @@ -25,6 +24,7 @@ class Culture (I.Culture, abstract.Culture):
def __init__(self,
*,
acquaintance_network=None,
group_membership_network=None,
**kwargs):
"""Initialize the unique instance of Culture.
Expand All @@ -33,6 +33,9 @@ def __init__(self,
acquaintance_network: Graph
The Network of acquaintances which is managed by Culture
(default is None)
group_membership_network: DiGraph
The Network between Individiuals and groups, which is managed
by Culture (default is None)
**kwargs
keyword arguments passed to super()
Expand All @@ -43,7 +46,14 @@ def __init__(self,
acquaintance_network = Graph()
assert isinstance(acquaintance_network, Graph)
self.acquaintance_network = acquaintance_network

if group_membership_network is None:
group_membership_network = DiGraph()
assert isinstance(group_membership_network, DiGraph)
self.group_membership_network = group_membership_network

self._worlds = set()
self._groups = set()

# make sure all variable values are valid:
self.assert_valid()
Expand All @@ -54,6 +64,11 @@ def worlds(self):
"""Get the set of all Worlds this Culture acts in."""
return self._worlds

@property # read-only
def groups(self):
"""Get the set of all Groups in this Culture."""
return self._groups

# no process-related methods

processes = [] # no processes in base
140 changes: 140 additions & 0 deletions pycopancore/model_components/base/implementation/group.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
""" """

# This file is part of pycopancore.
#
# Copyright (C) 2016-2017 by COPAN team at Potsdam Institute for Climate
# Impact Research
#
# URL: <http://www.pik-potsdam.de/copan/software>
# Contact: [email protected]
# License: BSD 2-clause license

# only used in this component, not in others:
from ... import abstract
from .... import master_data_model as D
from ....private import unknown

from .. import interface as I


class Group (I.Group, abstract.Group):
"""Gropp entity type mixin implementation class.
Base component's Group mixin that every model must use in composing
their Group class. Inherits from I.Group as the interface with all
necessary variables and parameters.
"""

# standard methods:

def __init__(self,
*,
culture,
world,
**kwargs
):
"""Initialize an instance of Group.
Parameters
----------
culture: obj
Culture the Group belongs to
world: obj
World the Group belongs to (to bypass AttributeErrors for now)
**kwargs
keyword arguments passed to super()
"""
super().__init__(**kwargs) # must be the first line

# init and set variables implemented via properties:
self._culture = None
self.culture = culture
self._world = None
self.world = world

if self.culture:
self.culture.group_membership_network.add_node(self, type="Group", color="green")

def deactivate(self):
"""Deactivate a group.
In particular, deregister from all networks.
"""
# deregister from all networks:
if self.culture:
self.culture.group_membership_network.remove_node(self)
super().deactivate() # must be the last line

def reactivate(self):
"""Reactivate a group.
In particular, deregister with all mandatory networks.
"""
super().reactivate() # must be the first line
# reregister with all mandatory networks:
if self.culture:
self.culture.group_membership_network.add_node(self, type="Group", color="green")


# getters and setters for references:

#culture needs to be before world, as group gets its world etc. over its culture
@property
def culture(self):
"""Get culture group is part of."""
return self._culture

@culture.setter
def culture(self, c):
"""Set culture group is part of."""
if self._culture is not None:
# first deregister from previous culture's list of worlds:
self._culture.groups.remove(self)
if c is not None:
assert isinstance(c, I.Culture), \
"Culture must be taxon type Culture"
c._groups.add(self)
self._culture = c

@property
def world(self):
"""Get the World the Group is part of."""
return self._world

@world.setter
def world(self, w):
"""Set the World the Group is part of."""
if self._world is not None:
# first deregister from previous world's list of cells:
self._world.groups.remove(self)
assert isinstance(w, I.World), "world must be of entity type World"
w._groups.add(self)
self._world = w

# getters for backwards references and convenience variables:

@property # read-only
def environment(self):
"""Get the Environment of which the Group is a part."""
return self._world.environment

@property # read-only
def metabolism(self):
"""Get the Metabolism of which the Group is a part."""
return self._world.metabolism

@property
def group_members(self):
"""Get the set of Individuals associated with this Group."""
# return self.culture.group_membership_network.neighbors(self)
return self.culture.group_membership_network.predecessors(self) # .predecessors as network is directed from inds to groups


# no process-related methods

processes = [] # no processes in base


Loading

0 comments on commit d937258

Please sign in to comment.