Source code for pyfeyn2.feynmandiagram

import logging
from dataclasses import dataclass, field
from typing import List, Optional, Union

import cssutils
from xsdata.formats.converter import Converter, converter

from pyfeyn2.particles import get_name

# We don't want to see the cssutils warnings, since we have custom properties
cssutils.log.setLevel(logging.CRITICAL)


# from pyfeyn2.propagator import Propagator
# from pyfeyn2.vertex import Vertex


@dataclass
[docs]class PDG:
[docs] pdgid: Optional[int] = field( default=21, metadata={"xml_attribute": True, "type": "Attribute"} )
[docs] type: Optional[str] = field( default="", metadata={"xml_attribute": True, "type": "Attribute"} )
[docs] latexname: Optional[str] = field( default=None, metadata={"xml_attribute": True, "type": "Attribute"} )
[docs] def _sync_latexname(self): """Sync the latexname with the pdgid""" if self.pdgid is not None: self.latexname = get_name(self.pdgid)
# def __post_init__(self): # self._sync_latexname()
[docs] def set_pdgid(self, pdgid): self.pdgid = pdgid self._sync_latexname() return self
[docs] def set_type(self, typ): self.type = typ return self
[docs]id = 0
@dataclass
[docs]class Identifiable:
[docs] id: Optional[str] = field( default=None, metadata={"xml_attribute": True, "type": "Attribute"} )
[docs] def __post_init__(self): global id if self.id is None: # use some global counter to generate unique id self.id = self.__class__.__name__ + str(id) id = id + 1
@dataclass
[docs]class Labeled:
[docs] label: Optional[str] = field( default=None, metadata={"xml_attribute": True, "type": "Attribute"} )
[docs] def set_label(self, label): self.label = label return self
@dataclass
[docs]class Texted:
[docs] text: Optional[str] = field( default="", metadata={"xml_attribute": True, "type": "Attribute"} )
[docs] def set_text(self, text): self.text = text return self
@dataclass
[docs]class Point:
[docs] x: Optional[float] = field( default=None, metadata={"xml_attribute": True, "type": "Attribute"} )
[docs] y: Optional[float] = field( default=None, metadata={"xml_attribute": True, "type": "Attribute"} )
[docs] z: Optional[float] = field( default=None, metadata={"xml_attribute": True, "type": "Attribute"} )
[docs] def set_point(self, p): self.x = float(p.x) self.y = float(p.y) return self
[docs] def set_xy(self, x, y): self.x = float(x) self.y = float(y) return self
[docs] def set_xyz(self, x, y, z): self.x = float(x) self.y = float(y) self.z = float(z) return self
[docs]CSSString = cssutils.css.CSSStyleDeclaration
@dataclass
[docs]class Styled:
[docs] style: CSSString = field( default_factory=lambda: cssutils.parseStyle(""), metadata={"name": "style", "xml_attribute": True, "type": "Attribute"}, )
[docs] def raw_style(self): return self.style.cssText.replace("\n", " ")
[docs] def put_style(self, key, value): if self.style is not None: self.style.setProperty(key, value) return self
[docs]class CSSConverter(Converter): @staticmethod
[docs] def deserialize(value: str, **kwargs) -> CSSString: return cssutils.parseStyle(value)
@staticmethod
[docs] def serialize(value: CSSString, **kwargs) -> str: return value.cssText.replace("\n", " ")
converter.register_converter(CSSString, CSSConverter()) @dataclass
[docs]class Bending:
[docs] bend: Optional[float] = field( default=None, metadata={"xml_attribute": True, "type": "Attribute"} )
@dataclass
[docs]class Targeting:
[docs] target: Optional[str] = field( default="", metadata={"xml_attribute": True, "type": "Attribute"} )
[docs] def set_target(self, target): self.target = target.id return self
@dataclass
[docs]class Sourcing:
[docs] source: Optional[str] = field( default="", metadata={"xml_attribute": True, "type": "Attribute"} )
[docs] def set_source(self, source): self.source = source.id return self
@dataclass
[docs]class Line(Targeting, Sourcing):
[docs] def connect(self, source, target): self.set_source(source) self.set_target(target) return self
@dataclass
[docs]class Vertex(Labeled, Point, Styled, Identifiable): pass
@dataclass
[docs]class Connector(Labeled, Bending, PDG, Styled, Identifiable):
[docs] momentum: Optional[str] = field( default=None, metadata={"xml_attribute": True, "type": "Attribute"} )
[docs] tension: Optional[float] = field( default=None, metadata={"xml_attribute": True, "type": "Attribute"} )
[docs] length: Optional[float] = field( default=None, metadata={"xml_attribute": True, "type": "Attribute"} )
[docs] def set_momentum(self, momentum): self.momentum = momentum return self
[docs] def set_tension(self, tension): self.tension = tension return self
[docs] def set_length(self, length): self.length = length return self
pass
@dataclass
[docs]class Leg(Point, Targeting, Connector):
[docs] sense: str = field( default="", metadata={"xml_attribute": True, "type": "Attribute"} )
[docs] def set_incoming(self): self.sense = "incoming" return self
[docs] def set_outgoing(self): self.sense = "outgoing" return self
@dataclass
[docs]class Propagator(Line, Connector): pass
@dataclass
[docs]class Label(Point, Texted, Identifiable): pass
@dataclass
[docs]class FeynmanDiagram:
[docs] class Meta:
[docs] name = "diagram"
[docs] propagators: List[Propagator] = field( default_factory=list, metadata={"name": "propagator", "type": "Element", "namespace": ""}, )
[docs] vertices: List[Vertex] = field( default_factory=list, metadata={"name": "vertex", "type": "Element", "namespace": ""}, )
[docs] legs: List[Leg] = field( default_factory=list, metadata={"name": "leg", "type": "Element", "namespace": ""}, )
[docs] labels: List[Label] = field( default_factory=list, metadata={"name": "label", "type": "Element", "namespace": ""}, )
[docs] def add(self, *fd_all: List[Union[Propagator, Vertex, Leg, Label]]): for a in fd_all: if isinstance(a, Propagator): self.propagators.append(a) elif isinstance(a, Vertex): self.vertices.append(a) elif isinstance(a, Leg): self.legs.append(a) elif isinstance(a, Label): self.labels.append(a) else: raise Exception("Unknown type: " + str(type(a)) + " " + str(a)) return self
[docs] def get_point(self, id): for v in self.vertices: if v.id == id: return v for l in self.legs: if l.id == id: return l return None
[docs] def get_bounding_box(self): min_x = 0 min_y = 0 max_x = 0 max_y = 0 for v in self.vertices: min_x = min(min_x, v.x) min_y = min(min_y, v.y) max_x = max(max_x, v.x) max_y = max(max_y, v.y) for l in self.legs: min_x = min(min_x, l.x) min_y = min(min_y, l.y) max_x = max(max_x, l.x) max_y = max(max_y, l.y) return min_x, min_y, max_x, max_y
@dataclass
[docs]class Meta:
[docs] class Meta:
[docs] name = "meta"
[docs] name: Optional[str] = field( default="", metadata={"xml_attribute": True, "type": "Attribute"} )
[docs] value: Optional[str] = field( default="", metadata={"xml_attribute": True, "type": "Attribute"} )
[docs]aliasMeta = Meta
@dataclass @dataclass
[docs]class FeynML:
[docs] class Meta:
[docs] name = "feynml"
[docs] head: List[Head] = field( default_factory=list, metadata={"name": "head", "namespace": ""} )
[docs] diagrams: List[FeynmanDiagram] = field( default_factory=list, metadata={"name": "diagram", "type": "Element", "namespace": ""}, )
[docs] def get_diagram(self, id): for d in self.diagrams: if d.id == id: return d return None